ges: Big PiTiVi formatter cleanup
authorThibault Saunier <thibault.saunier@collabora.com>
Wed, 2 Nov 2011 16:52:16 +0000 (13:52 -0300)
committerThibault Saunier <thibault.saunier@collabora.com>
Wed, 11 Jan 2012 14:56:15 +0000 (11:56 -0300)
Also set the version to 0.2

ges/ges-pitivi-formatter.c

index 0909554754d784433c4e25de6c066a630b82ffa5..42ff823347d0a047e6ca955106b6a2cdf56b9310 100644 (file)
@@ -1,58 +1,74 @@
 #include <ges/ges.h>
+#include <inttypes.h>
 #include <unistd.h>
 #define GetCurrentDir getcwd
 
 G_DEFINE_TYPE (GESPitiviFormatter, ges_pitivi_formatter, GES_TYPE_FORMATTER);
 
+GST_DEBUG_CATEGORY_STATIC (ges_pitivi_formatter_debug);
+#define GST_CAT_DEFAULT ges_pitivi_formatter_debug
+
+/* The PiTiVi etree formatter is 0.1 we set GES one to 0.2 */
+#define VERSION "0.2"
+
 static gboolean save_pitivi_timeline_to_uri (GESFormatter * pitivi_formatter,
     GESTimeline * timeline, const gchar * uri);
 static gboolean load_pitivi_file_from_uri (GESFormatter * self,
     GESTimeline * timeline, const gchar * uri);
-
 static void ges_pitivi_formatter_finalize (GObject * object);
 
-static xmlDocPtr create_doc (const gchar * uri);
-
-static GHashTable *get_nodes_infos (xmlNodePtr nodes);
-static gboolean create_tracks (GESFormatter * self);
-static GHashTable *list_sources (GESFormatter * self);
-static gboolean parse_track_objects (GESFormatter * self);
-static gboolean parse_timeline_objects (GESFormatter * self);
-
-static void save_tracks (GESTimeline * timeline, xmlTextWriterPtr writer,
-    GList * source_list);
-static GList *save_sources (GESTimelineLayer * layer, xmlTextWriterPtr writer);
-static void save_track_objects (xmlTextWriterPtr writer, GList * source_list,
-    gchar * res, gint * id);
-static void save_timeline_objects (xmlTextWriterPtr writer, GList * list);
-static void destroy_all (GList * list);
-static void create_new_source_table (gchar * key, gchar * value,
-    GHashTable * table);
-static gboolean make_timeline_objects (GESFormatter * self);
-void set_properties (GObject * obj, GHashTable * props_table);
-void make_source (GList * ref_list,
-    GHashTable * source_table, GESFormatter * self);
-
-void layers_table_destroyer (gpointer data, gpointer data2, void *unused);
-void list_table_destroyer (gpointer data, gpointer data2, void *unused);
-void destroyer (gpointer data, gpointer data2, void *unused);
-void ultimate_table_destroyer (gpointer data, gpointer data2, void *unused);
-static void
-track_object_added_cb (GESTimelineObject * object,
-    GESTrackObject * track_object, GHashTable * props_table);
+
+typedef struct SrcMapping
+{
+  gchar *id;
+  GESTimelineObject *obj;
+  guint priority;
+  GList *tck_obj_ids;
+} SrcMapping;
 
 struct _GESPitiviFormatterPrivate
 {
-  gint not_done;
   xmlXPathContextPtr xpathCtx;
-  GHashTable *source_table, *track_objects_table, *timeline_objects_table,
-      *layers_table;
+
+  /* {"sourceId" : {"prop": "value"}} */
+  GHashTable *source_table;
+
+  /* {trackId: {"factory_ref": factoryId, ""}
+   * if effect:
+   *      {"factory_ref": "effect",
+   *       "effect_name": name
+   *       "effect_props": {"propname": value}}}
+   */
+  GHashTable *track_objects_table;
+
+  /* {factory-ref: [track-object-ref-id,...]} */
+  GHashTable *timeline_objects_table;
+
+  /* {layerPriority: layer} */
+  GHashTable *layers_table;
+
   GESTimeline *timeline;
-  gboolean parsed;
+
   GESTrack *tracka, *trackv;
-  GESTimelineTestSource *background;
 };
 
+/* Memory freeing functions */
+static void
+free_src_map (SrcMapping * srcmap)
+{
+  g_free (srcmap->id);
+  g_object_unref (srcmap->obj);
+  g_list_free_full (srcmap->tck_obj_ids, (GDestroyNotify) g_free);
+  g_slice_free (SrcMapping, srcmap);
+}
+
+static void
+list_table_destroyer (gpointer key, gpointer value, void *unused)
+{
+  g_list_free_full (value, g_free);
+}
+
+/* Object functions */
 static void
 ges_pitivi_formatter_class_init (GESPitiviFormatterClass * klass)
 {
@@ -70,21 +86,27 @@ ges_pitivi_formatter_class_init (GESPitiviFormatterClass * klass)
 static void
 ges_pitivi_formatter_init (GESPitiviFormatter * self)
 {
+  GESPitiviFormatterPrivate *priv;
+
+  GST_DEBUG_CATEGORY_INIT (ges_pitivi_formatter_debug, "ges_pitivi_formatter",
+      GST_DEBUG_FG_YELLOW, "ges pitivi formatter");
+
   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
       GES_TYPE_PITIVI_FORMATTER, GESPitiviFormatterPrivate);
 
-  self->priv->not_done = 0;
+  priv = self->priv;
 
-  self->priv->track_objects_table =
-      g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
+  priv->track_objects_table =
+      g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
+      (GDestroyNotify) g_hash_table_destroy);
 
-  self->priv->timeline_objects_table =
-      g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
+  priv->timeline_objects_table =
+      g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
 
-  self->priv->layers_table =
-      g_hash_table_new_full (g_int_hash, g_str_equal, NULL, NULL);
+  priv->layers_table =
+      g_hash_table_new_full (g_int_hash, g_str_equal, g_free, g_object_unref);
 
-  self->priv->parsed = FALSE;
+  priv->source_table = NULL;
 }
 
 static void
@@ -94,350 +116,108 @@ ges_pitivi_formatter_finalize (GObject * object)
   GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
 
   if (priv->source_table != NULL) {
-    g_hash_table_foreach (priv->source_table,
-        (GHFunc) ultimate_table_destroyer, NULL);
     g_hash_table_destroy (priv->source_table);
   }
 
   if (priv->timeline_objects_table != NULL) {
     g_hash_table_foreach (priv->timeline_objects_table,
         (GHFunc) list_table_destroyer, NULL);
-    g_hash_table_destroy (priv->timeline_objects_table);
   }
 
-  if (priv->layers_table != NULL) {
-    g_hash_table_foreach (priv->layers_table,
-        (GHFunc) layers_table_destroyer, NULL);
+  if (priv->layers_table != NULL)
     g_hash_table_destroy (priv->layers_table);
-  }
 
   if (priv->track_objects_table != NULL) {
-    g_hash_table_foreach (priv->track_objects_table,
-        (GHFunc) ultimate_table_destroyer, NULL);
     g_hash_table_destroy (priv->track_objects_table);
   }
 
   G_OBJECT_CLASS (ges_pitivi_formatter_parent_class)->finalize (object);
 }
 
-static gboolean
-save_pitivi_timeline_to_uri (GESFormatter * pitivi_formatter,
-    GESTimeline * timeline, const gchar * uri)
-{
-  xmlTextWriterPtr writer;
-  GList *list = NULL, *layers = NULL, *tmp = NULL;
-  writer = xmlNewTextWriterFilename (uri, 0);
-
-  xmlTextWriterSetIndent (writer, 1);
-  xmlTextWriterStartElement (writer, BAD_CAST "pitivi");
-
-  layers = ges_timeline_get_layers (timeline);
-  xmlTextWriterStartElement (writer, BAD_CAST "factories");
-  xmlTextWriterStartElement (writer, BAD_CAST "sources");
-  for (tmp = layers; tmp; tmp = tmp->next) {
-
-    /* 99 is the priority of the background source. */
-    if (ges_timeline_layer_get_priority (tmp->data) != 99) {
-      list = save_sources (tmp->data, writer);
-    }
-  }
-  xmlTextWriterEndElement (writer);
-  xmlTextWriterEndElement (writer);
-  save_tracks (timeline, writer, list);
-  save_timeline_objects (writer, list);
-  xmlTextWriterEndDocument (writer);
-  xmlFreeTextWriter (writer);
-
-  g_list_free (layers);
-  g_list_foreach (list, (GFunc) destroy_all, NULL);
-  g_list_free (list);
-
-  return TRUE;
-}
-
-static gboolean
-load_pitivi_file_from_uri (GESFormatter * self,
-    GESTimeline * timeline, const gchar * uri)
-{
-  xmlDocPtr doc;
-  GESTimelineLayer *layer;
-  GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
-
-  gboolean ret = TRUE;
-  gint *prio = malloc (sizeof (gint));
-
-  *prio = 0;
-  layer = ges_timeline_layer_new ();
-  g_object_set (layer, "auto-transition", TRUE, NULL);
-
-  g_hash_table_insert (priv->layers_table, prio, layer);
-  priv->timeline = timeline;
-  g_object_set (layer, "priority", (gint32) 0, NULL);
-
-  if (!ges_timeline_add_layer (timeline, layer)) {
-    GST_ERROR ("Couldn't add layer");
-    return FALSE;
-  }
-
-  if (!(doc = create_doc (uri))) {
-    GST_ERROR ("The xptv file for uri %s was badly formed or did not exist",
-        uri);
-    return FALSE;
-  }
-
-  priv->xpathCtx = xmlXPathNewContext (doc);
-
-  if (!create_tracks (self)) {
-    GST_ERROR ("Couldn't create tracks");
-    ret = FALSE;
-    goto fail;
-  }
-
-  priv->source_table = list_sources (self);
-
-  if (!parse_timeline_objects (self)) {
-    GST_ERROR ("Couldn't find timeline objects markup in the xptv file");
-    ret = FALSE;
-    goto fail;
-  }
-
-  if (!parse_track_objects (self)) {
-    GST_ERROR ("Couldn't find track objects markup in the xptv file");
-    ret = FALSE;
-  }
-
-  if (!make_timeline_objects (self))
-    ret = FALSE;
-
-fail:
-  xmlXPathFreeContext (priv->xpathCtx);
-  xmlFreeDoc (doc);
-  return ret;
-}
-
-static void
-save_timeline_objects (xmlTextWriterPtr writer, GList * list)
+GESPitiviFormatter *
+ges_pitivi_formatter_new (void)
 {
-  GList *tmp;
-  gint n_objects, i;
-
-  xmlTextWriterStartElement (writer, BAD_CAST "timeline-objects");
-
-  for (tmp = list; tmp; tmp = tmp->next) {
-
-    GList *elem;
-    xmlChar *cast;
-
-    xmlTextWriterStartElement (writer, BAD_CAST "timeline-object");
-    elem = tmp->data;
-    xmlTextWriterStartElement (writer, BAD_CAST "factory-ref");
-    cast = g_list_first (elem)->data;
-    xmlTextWriterWriteAttribute (writer, BAD_CAST "id", BAD_CAST cast);
-    xmlTextWriterEndElement (writer);
-    xmlTextWriterStartElement (writer, BAD_CAST "track-object-refs");
-
-    n_objects = g_list_length (elem) - 4;
-    for (i = 0; i < n_objects; i++) {
-      xmlTextWriterStartElement (writer, BAD_CAST "track-object-ref");
-      xmlTextWriterWriteAttribute (writer, BAD_CAST "id",
-          BAD_CAST (gchar *) (g_list_nth (elem, (guint) 4 + i)->data));
-      xmlTextWriterEndElement (writer);
-    }
-    xmlTextWriterEndElement (writer);
-    xmlTextWriterEndElement (writer);
-  }
-  xmlTextWriterEndElement (writer);
+  return g_object_new (GES_TYPE_PITIVI_FORMATTER, NULL);
 }
 
-static GList *
-save_sources (GESTimelineLayer * layer, xmlTextWriterPtr writer)
-{
-  GList *objects, *tmp;
-  GHashTable *source_table;
-
-  GList *source_list = NULL;
-  int id = 1;
-  objects = ges_timeline_layer_get_objects (layer);
-  source_table =
-      g_hash_table_new_full (g_str_hash, g_int_equal, g_free, g_free);
-
-  for (tmp = objects; tmp; tmp = tmp->next) {
-    GList *ref_type_list = NULL;
-    GESTimelineObject *object;
-    gchar *tfs_uri;
-    xmlChar *cast;
-    object = tmp->data;
-
-    if GES_IS_TIMELINE_FILE_SOURCE
-      (object) {
-
-      tfs_uri = (gchar *) ges_timeline_filesource_get_uri
-          (GES_TIMELINE_FILE_SOURCE (object));
-
-      if (!g_hash_table_lookup (source_table, tfs_uri)) {
-        cast = xmlXPathCastNumberToString (id);
-        g_hash_table_insert (source_table, g_strdup (tfs_uri),
-            g_strdup ((gchar *) cast));
-        xmlFree (cast);
-        xmlTextWriterStartElement (writer, BAD_CAST "source");
-        xmlTextWriterWriteAttribute (writer, BAD_CAST "filename",
-            BAD_CAST tfs_uri);
-        cast = xmlXPathCastNumberToString (id);
-        xmlTextWriterWriteAttribute (writer, BAD_CAST "id", BAD_CAST cast);
-        xmlFree (cast);
-        xmlTextWriterEndElement (writer);
-        id++;
-      }
-
-      ref_type_list =
-          g_list_append (ref_type_list,
-          g_strdup (g_hash_table_lookup (source_table, tfs_uri)));
-      ref_type_list = g_list_append (ref_type_list, object);
-      ref_type_list = g_list_append (ref_type_list, g_strdup ("simple"));
-      ref_type_list =
-          g_list_append (ref_type_list,
-          GINT_TO_POINTER (ges_timeline_layer_get_priority (layer)));
-      source_list = g_list_append (source_list, g_list_copy (ref_type_list));
-      g_list_free (ref_type_list);
-      }
-  }
-
-  g_object_unref (G_OBJECT (layer));
-  g_list_free (objects);
-  g_hash_table_destroy (source_table);
-  return source_list;
-}
+/* Project saving functions */
 
-static void
-save_tracks (GESTimeline * timeline, xmlTextWriterPtr writer,
-    GList * source_list)
+static void inline
+write_int_attribute (xmlTextWriterPtr writer, guint64 nb, const gchar * attr,
+    const gchar * type)
 {
-  GList *tracks, *tmp;
-
-  gint id = 0;
-
-  xmlTextWriterStartElement (writer, BAD_CAST "timeline");
-  xmlTextWriterStartElement (writer, BAD_CAST "tracks");
-  tracks = ges_timeline_get_tracks (timeline);
-
-  for (tmp = tracks; tmp; tmp = tmp->next) {
-    gchar *type, *caps, *res;
-    GESTrack *track;
-    GValue v = { 0 };
-    track = GES_TRACK (tmp->data);
-    xmlTextWriterStartElement (writer, BAD_CAST "track");
-    xmlTextWriterStartElement (writer, BAD_CAST "stream");
-    g_value_init (&v, GES_TYPE_TRACK_TYPE);
-    g_object_get_property (G_OBJECT (track), "track-type", &v);
-    type = gst_value_serialize (&v);
-    caps = gst_caps_to_string (ges_track_get_caps (track));
-    xmlTextWriterWriteAttribute (writer, BAD_CAST "caps", BAD_CAST caps);
-    g_free (caps);
-
-    if (!g_strcmp0 (type, "GES_TRACK_TYPE_AUDIO")) {
-      xmlTextWriterWriteAttribute (writer, BAD_CAST "type",
-          BAD_CAST "pitivi.stream.AudioStream");
-      xmlTextWriterEndElement (writer);
-      res = (gchar *) "audio";
-    } else {
-      xmlTextWriterWriteAttribute (writer, BAD_CAST "type",
-          BAD_CAST "pitivi.stream.VideoStream");
-      xmlTextWriterEndElement (writer);
-      res = (gchar *) "video";
-    }
-    g_free (type);
-    save_track_objects (writer, source_list, res, &id);
-    xmlTextWriterEndElement (writer);
-  }
-
-  g_list_free (tracks);
-  xmlTextWriterEndElement (writer);
+  gchar *str = g_strdup_printf ("%s%" PRIu64, type, nb);
+  xmlTextWriterWriteAttribute (writer, BAD_CAST attr, BAD_CAST str);
+  g_free (str);
 }
 
 static void
-save_track_objects (xmlTextWriterPtr writer, GList * source_list, gchar * res,
-    gint * id)
+save_track_objects (xmlTextWriterPtr writer, GList * source_list,
+    GESTrackType type, gint * id)
 {
   GList *tmp, *tck_objs, *tmp_tck;
   gchar *bin_desc;
   xmlTextWriterStartElement (writer, BAD_CAST "track-objects");
 
+  GST_DEBUG ("Saving track objects");
   for (tmp = source_list; tmp; tmp = tmp->next) {
-    GList *elem;
+    SrcMapping *srcmap;
     GESTimelineObject *object;
-    guint i, n, j;
+    guint i, j;
+    guint64 inpoint, duration, start;
 
-    elem = tmp->data;
-    object = g_list_next (elem)->data;
-    tck_objs = ges_timeline_object_get_track_objects (object);
+    srcmap = (SrcMapping *) tmp->data;
+    object = srcmap->obj;
 
+    /* Save track associated objects */
+    tck_objs = ges_timeline_object_get_track_objects (object);
     for (tmp_tck = tck_objs; tmp_tck; tmp_tck = tmp_tck->next) {
-      GParamSpec **properties;
       xmlChar *cast;
-      gchar *prio_str;
+      GESTrackObject *tckobj = GES_TRACK_OBJECT (tmp_tck->data);
+      GESTrack *track = ges_track_object_get_track (tckobj);
+      const gchar *active, *locked;
 
-      if (!ges_track_object_is_active (tmp_tck->data)) {
+      if (!track) {
+        GST_WARNING ("Track object %p not in a track yet", tckobj);
         continue;
       }
 
-      if (((ges_track_object_get_track (tmp_tck->data)->type ==
-                  GES_TRACK_TYPE_VIDEO)
-              && (!g_strcmp0 (res, (gchar *) "video")))
-          || ((ges_track_object_get_track (tmp_tck->data)->type ==
-                  GES_TRACK_TYPE_AUDIO)
-              && (!g_strcmp0 (res, (gchar *) "audio")))) {
-      } else {
+      /* We are serializing this track type */
+      if (track->type != type)
         continue;
-      }
 
+      /* Save properties */
+      /* Set important properties */
       xmlTextWriterStartElement (writer, BAD_CAST "track-object");
-      cast =
-          xmlXPathCastNumberToString (GPOINTER_TO_INT (g_list_nth (elem,
-                  (guint) 3)->data));
-      prio_str = g_strconcat ((gchar *) "(int)", (gchar *) cast, NULL);
-      xmlFree (cast);
-      xmlTextWriterWriteAttribute (writer, BAD_CAST "priority",
-          BAD_CAST prio_str);
-      g_free (prio_str);
-      properties =
-          g_object_class_list_properties (G_OBJECT_GET_CLASS (tmp_tck->data),
-          &n);
-
-      for (i = 0; i < n; i++) {
-        GParamSpec *p = properties[i];
-        GValue v = { 0 };
-        gchar *serialized, *concatenated;
 
-        if (!g_strcmp0 (p->name, (gchar *) "duration") ||
-            !g_strcmp0 (p->name, (gchar *) "start") ||
-            !g_strcmp0 (p->name, (gchar *) "in-point")) {
-          g_value_init (&v, p->value_type);
-          g_object_get_property (G_OBJECT (tmp_tck->data), p->name, &v);
-          serialized = gst_value_serialize (&v);
-          concatenated = g_strconcat ((gchar *) "(gint64)", serialized, NULL);
+      active =
+          ges_track_object_is_active (tckobj) ? "(bool)True" : "(bool)False";
+      xmlTextWriterWriteAttribute (writer, BAD_CAST "active", BAD_CAST active);
+      locked =
+          ges_track_object_is_locked (tckobj) ? "(bool)True" : "(bool)False";
+      xmlTextWriterWriteAttribute (writer, BAD_CAST "locked", BAD_CAST locked);
+
+      /*  Here the priority correspond to the layer priority */
+      write_int_attribute (writer, srcmap->priority, "priority", "(int)");
+      g_object_get (G_OBJECT (tckobj), "duration", &duration, "start", &start,
+          "in-point", &inpoint, NULL);
+      write_int_attribute (writer, duration, "duration", "(gint64)");
+      write_int_attribute (writer, start, "start", "(gint64)");
+      write_int_attribute (writer, inpoint, "in_point", "(gint64)");
 
-          if (!g_strcmp0 (p->name, (gchar *) "in-point")) {
-            xmlTextWriterWriteAttribute (writer, BAD_CAST (gchar *) "in_point",
-                BAD_CAST concatenated);
-          } else {
-            xmlTextWriterWriteAttribute (writer, BAD_CAST p->name,
-                BAD_CAST concatenated);
-          }
-          g_free (concatenated);
-          g_free (serialized);
-        }
-      }
-      g_free (properties);
       cast = xmlXPathCastNumberToString (*id);
       xmlTextWriterWriteAttribute (writer, BAD_CAST "id", BAD_CAST cast);
       xmlFree (cast);
 
-      if (GES_IS_TRACK_EFFECT (tmp_tck->data)) {
+      if (GES_IS_TRACK_EFFECT (tckobj)) {
         GParamSpec **pspecs, *spec;
         gchar *serialized, *concatenated;
         guint n_props = 0;
 
-        g_object_get (tmp_tck->data, "bin-description", &bin_desc, NULL);
+        xmlTextWriterWriteAttribute (writer, BAD_CAST "type",
+            BAD_CAST "pitivi.timeline.track.TrackEffect");
+
+        g_object_get (tckobj, "bin-description", &bin_desc, NULL);
         xmlTextWriterStartElement (writer, BAD_CAST "effect");
         xmlTextWriterStartElement (writer, BAD_CAST "factory");
         xmlTextWriterWriteAttribute (writer, BAD_CAST "name",
@@ -445,17 +225,16 @@ save_track_objects (xmlTextWriterPtr writer, GList * source_list, gchar * res,
         xmlTextWriterEndElement (writer);
         xmlTextWriterStartElement (writer, BAD_CAST "gst-element-properties");
 
-        pspecs =
-            ges_track_object_list_children_properties (tmp_tck->data, &n_props);
+        pspecs = ges_track_object_list_children_properties (tckobj, &n_props);
 
         j = 0;
 
         while (j < n_props) {
           GValue val = { 0 };
+
           spec = pspecs[j];
           g_value_init (&val, spec->value_type);
-          ges_track_object_get_child_property_by_pspec (tmp_tck->data, spec,
-              &val);
+          ges_track_object_get_child_property_by_pspec (tckobj, spec, &val);
           serialized = gst_value_serialize (&val);
           if (!g_strcmp0 (spec->name, (gchar *) "preset")) {
             concatenated =
@@ -482,17 +261,23 @@ save_track_objects (xmlTextWriterPtr writer, GList * source_list, gchar * res,
         g_free (pspecs);
 
       } else {
+        xmlTextWriterWriteAttribute (writer, BAD_CAST "type",
+            BAD_CAST "pitivi.timeline.track.SourceTrackObject");
+
         xmlTextWriterStartElement (writer, BAD_CAST "factory-ref");
-        cast = g_list_first (elem)->data;
-        xmlTextWriterWriteAttribute (writer, BAD_CAST "id", BAD_CAST cast);
+        xmlTextWriterWriteAttribute (writer, BAD_CAST "id",
+            BAD_CAST srcmap->id);
       }
       xmlTextWriterEndElement (writer);
       xmlTextWriterEndElement (writer);
 
-      if (GES_IS_TRACK_EFFECT (tmp_tck->data)) {
-        elem = g_list_append (elem, xmlXPathCastNumberToString (*id));
+      /* We add effects at the end of the trackobject list */
+      if (GES_IS_TRACK_EFFECT (tckobj)) {
+        srcmap->tck_obj_ids = g_list_append (srcmap->tck_obj_ids,
+            xmlXPathCastNumberToString (*id));
       } else {
-        elem = g_list_insert (elem, xmlXPathCastNumberToString (*id), 4);
+        srcmap->tck_obj_ids = g_list_prepend (srcmap->tck_obj_ids,
+            xmlXPathCastNumberToString (*id));
       }
       *id = *id + 1;
     }
@@ -501,244 +286,272 @@ save_track_objects (xmlTextWriterPtr writer, GList * source_list, gchar * res,
   xmlTextWriterEndElement (writer);
 }
 
-static xmlDocPtr
-create_doc (const gchar * uri)
+static void
+save_tracks (GESTimeline * timeline, xmlTextWriterPtr writer,
+    GList * source_list)
 {
-  xmlDocPtr doc;
-  doc = xmlParseFile (uri);
-  return doc;
-}
+  GList *tracks, *tmp;
 
-static GHashTable *
-list_sources (GESFormatter * self)
-{
-  GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
-  xmlXPathObjectPtr xpathObj;
-  GHashTable *table, *sources_table;
-  int size, j;
-  gchar *id;
-  xmlNodeSetPtr nodes;
+  gint id = 0;
 
-  sources_table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
-  xpathObj = xmlXPathEvalExpression ((const xmlChar *)
-      "/pitivi/factories/sources/source", priv->xpathCtx);
-  nodes = xpathObj->nodesetval;
-  size = (nodes) ? nodes->nodeNr : 0;
-  for (j = 0; j < size; ++j) {
-    table = get_nodes_infos (nodes->nodeTab[j]);
-    id = (gchar *) g_hash_table_lookup (table, (gchar *) "id");
-    g_hash_table_insert (sources_table, g_strdup (id), table);
-  }
+  xmlTextWriterStartElement (writer, BAD_CAST "timeline");
+  xmlTextWriterStartElement (writer, BAD_CAST "tracks");
 
-  xmlXPathFreeObject (xpathObj);
-  return sources_table;
-}
+  GST_DEBUG ("Saving tracks");
 
-static gboolean
-make_timeline_objects (GESFormatter * self)
-{
-  GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
-  GHashTable *source_table;
-  GESTimelineLayer *back_layer;
-  gint i;
-  gint *prio = malloc (sizeof (gint));
+  tracks = ges_timeline_get_tracks (timeline);
+  for (tmp = tracks; tmp; tmp = tmp->next) {
+    gchar *caps;
+    GESTrackType type;
 
-  GList *keys = NULL, *tmp = NULL, *ref_list = NULL;
+    GESTrack *track = GES_TRACK (tmp->data);
 
-  *prio = 0;
+    xmlTextWriterStartElement (writer, BAD_CAST "track");
+    xmlTextWriterStartElement (writer, BAD_CAST "stream");
 
-  priv->background = ges_timeline_test_source_new ();
-  back_layer = ges_timeline_layer_new ();
-  ges_timeline_layer_set_priority (back_layer, 99);
-  if (!ges_timeline_add_layer (priv->timeline, back_layer)) {
-    GST_ERROR ("Couldn't add layer");
-    return FALSE;
-  }
+    /* Serialize track type and caps */
+    g_object_get (G_OBJECT (track), "track-type", &type, NULL);
+    caps = gst_caps_to_string (ges_track_get_caps (track));
+    xmlTextWriterWriteAttribute (writer, BAD_CAST "caps", BAD_CAST caps);
+    g_free (caps);
 
-  if (!ges_timeline_layer_add_object (back_layer,
-          GES_TIMELINE_OBJECT (priv->background))) {
-    GST_ERROR ("Couldn't add background to the layer");
-    return FALSE;
-  }
+    if (type == GES_TRACK_TYPE_AUDIO) {
+      xmlTextWriterWriteAttribute (writer, BAD_CAST "type",
+          BAD_CAST "pitivi.stream.AudioStream");
+      xmlTextWriterEndElement (writer);
+    } else if (type == GES_TRACK_TYPE_VIDEO) {
+      xmlTextWriterWriteAttribute (writer, BAD_CAST "type",
+          BAD_CAST "pitivi.stream.VideoStream");
+      xmlTextWriterEndElement (writer);
+    } else {
+      GST_WARNING ("Track type %i not supported", type);
 
-  keys = g_hash_table_get_keys (priv->timeline_objects_table);
+      continue;
+    }
 
-  for (tmp = keys, i = 1; tmp; tmp = tmp->next, i++) {
-    if (i == g_list_length (keys))
-      priv->parsed = TRUE;
-    ref_list =
-        g_hash_table_lookup (priv->timeline_objects_table, (gchar *) tmp->data);
-    source_table =
-        g_hash_table_lookup (priv->source_table, (gchar *) tmp->data);
-    make_source (ref_list, source_table, self);
+    save_track_objects (writer, source_list, type, &id);
+    xmlTextWriterEndElement (writer);
   }
-  g_hash_table_insert (priv->layers_table, prio, back_layer);
-  free (prio);
 
-  g_list_free (keys);
-  return TRUE;
+  g_list_free_full (tracks, g_object_unref);
+  xmlTextWriterEndElement (writer);
 }
 
-void
-make_source (GList * ref_list, GHashTable * source_table, GESFormatter * self)
+static GList *
+save_sources (GList * layers, xmlTextWriterPtr writer)
 {
-  GHashTable *props_table, *effect_table;
-  gchar **prio_array;
+  GList *tlobjects, *tmp, *tmplayer;
   GESTimelineLayer *layer;
-  GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
+  GHashTable *source_table;
 
-  gchar *fac_ref = NULL, *media_type = NULL, *filename = NULL;
-  GList *tmp = NULL, *keys, *tmp_key;
-  GESTimelineFileSource *src = NULL;
-  gint cast_prio = 0;
-  gint *prio = malloc (sizeof (gint));
-  gboolean a_avail = FALSE, v_avail = FALSE, video = FALSE;
-
-  for (tmp = ref_list; tmp; tmp = tmp->next) {
-    props_table =
-        g_hash_table_lookup (priv->track_objects_table, (gchar *) tmp->data);
-    prio_array =
-        g_strsplit ((gchar *) g_hash_table_lookup (props_table,
-            (gchar *) "priority"), ")", 0);
-    cast_prio = (gint) g_ascii_strtod (prio_array[1], NULL);
-    *prio = cast_prio;
-    fac_ref = (gchar *) g_hash_table_lookup (props_table, (gchar *) "fac_ref");
-    media_type =
-        (gchar *) g_hash_table_lookup (props_table, (gchar *) "media_type");
+  GList *source_list = NULL;
+  int id = 1;
 
-    g_strfreev (prio_array);
+  GST_DEBUG ("Saving sources");
 
-    if (!g_strcmp0 (media_type, (gchar *) "pitivi.stream.VideoStream"))
-      video = TRUE;
-    else
-      video = FALSE;
+  source_table =
+      g_hash_table_new_full (g_str_hash, g_int_equal, g_free, g_free);
 
-    if (!(layer = g_hash_table_lookup (priv->layers_table, &cast_prio))) {
-      layer = ges_timeline_layer_new ();
-      g_object_set (layer, "auto-transition", TRUE, NULL);
-      ges_timeline_layer_set_priority (layer, cast_prio);
-      ges_timeline_add_layer (priv->timeline, layer);
-      g_hash_table_insert (priv->layers_table, prio, layer);
-      free (prio);
-    }
+  for (tmplayer = layers; tmplayer; tmplayer = tmplayer->next) {
+    layer = GES_TIMELINE_LAYER (tmplayer->data);
 
-    if (g_strcmp0 (fac_ref, (gchar *) "effect") && a_avail && (!video)) {
-      a_avail = FALSE;
-      g_signal_connect (src, "track-object-added",
-          G_CALLBACK (track_object_added_cb), props_table);
-
-    } else if (g_strcmp0 (fac_ref, (gchar *) "effect") && v_avail && (video)) {
-      v_avail = FALSE;
-      g_signal_connect (src, "track-object-added",
-          G_CALLBACK (track_object_added_cb), props_table);
-
-    } else if (g_strcmp0 (fac_ref, (gchar *) "effect")) {
-      char cCurrentPath[FILENAME_MAX], *path;
-
-      if (a_avail) {
-        ges_timeline_filesource_set_supported_formats (src,
-            GES_TRACK_TYPE_VIDEO);
-      } else if (v_avail) {
-        ges_timeline_filesource_set_supported_formats (src,
-            GES_TRACK_TYPE_AUDIO);
-      }
+    tlobjects = ges_timeline_layer_get_objects (layer);
+    for (tmp = tlobjects; tmp; tmp = tmp->next) {
+      SrcMapping *srcmap = g_slice_new0 (SrcMapping);
+      GESTimelineObject *tlobj;
+      gchar *tfs_uri;
+      tlobj = tmp->data;
 
-      filename =
-          (gchar *) g_hash_table_lookup (source_table, (gchar *) "filename");
-      path = GetCurrentDir (cCurrentPath, sizeof (cCurrentPath));
+      if (GES_IS_TIMELINE_FILE_SOURCE (tlobj)) {
 
-      if (!g_strcmp0 (filename, (gchar *) "/DJ5r3oNFVeE.flv") ||
-          !g_strcmp0 (filename, (gchar *) "/a1Y73sPHKxw.flv")) {
-        filename = g_strconcat ("file://", path, filename, NULL);
-        src = ges_timeline_filesource_new (filename);
-        g_free (filename);
-      } else {
-        src = ges_timeline_filesource_new (filename);
-      }
+        tfs_uri = (gchar *) ges_timeline_filesource_get_uri
+            (GES_TIMELINE_FILE_SOURCE (tlobj));
 
-      if (!video) {
-        v_avail = TRUE;
-        a_avail = FALSE;
-      } else {
-        a_avail = TRUE;
-        v_avail = FALSE;
+        if (!g_hash_table_lookup (source_table, tfs_uri)) {
+          gchar *strid = g_strdup_printf ("%i", id);
+
+          g_hash_table_insert (source_table, g_strdup (tfs_uri), strid);
+          xmlTextWriterStartElement (writer, BAD_CAST "source");
+          xmlTextWriterWriteAttribute (writer, BAD_CAST "filename",
+              BAD_CAST tfs_uri);
+          xmlTextWriterWriteAttribute (writer, BAD_CAST "id", BAD_CAST strid);
+          xmlTextWriterEndElement (writer);
+          id++;
+        }
+
+        srcmap->id = g_strdup (g_hash_table_lookup (source_table, tfs_uri));
+        srcmap->obj = g_object_ref (tlobj);
+        srcmap->priority = ges_timeline_layer_get_priority (layer);
+        /* We fill up the tck_obj_ids in save_track_objects */
+        source_list = g_list_append (source_list, srcmap);
       }
-      set_properties (G_OBJECT (src), props_table);
-      ges_timeline_layer_add_object (layer, GES_TIMELINE_OBJECT (src));
+    }
+    g_list_free_full (tlobjects, g_object_unref);
+    g_object_unref (G_OBJECT (layer));
+  }
+  g_hash_table_destroy (source_table);
 
-    } else if (!g_strcmp0 (fac_ref, (gchar *) "effect")) {
-      GESTrackParseLaunchEffect *effect;
-      gchar *active = (gchar *)
-          g_hash_table_lookup (props_table, (gchar *) "active");
+  return source_list;
+}
 
-      effect = ges_track_parse_launch_effect_new ((gchar *)
-          g_hash_table_lookup (props_table, (gchar *) "effect_name"));
-      effect_table =
-          g_hash_table_lookup (props_table, (gchar *) "effect_props");
+static void
+save_timeline_objects (xmlTextWriterPtr writer, GList * list)
+{
+  GList *tmp, *tck_obj_ids;
 
-      ges_timeline_object_add_track_object (GES_TIMELINE_OBJECT (src),
-          GES_TRACK_OBJECT (effect));
+  xmlTextWriterStartElement (writer, BAD_CAST "timeline-objects");
 
-      if (!g_strcmp0 (active, (gchar *) "(bool)False"))
-        ges_track_object_set_active (GES_TRACK_OBJECT (effect), FALSE);
-      if (video)
-        ges_track_add_object (priv->trackv, GES_TRACK_OBJECT (effect));
-      else
-        ges_track_add_object (priv->tracka, GES_TRACK_OBJECT (effect));
-      keys = g_hash_table_get_keys (effect_table);
+  GST_DEBUG ("Saving timeline objects");
 
-      for (tmp_key = keys; tmp_key; tmp_key = tmp_key->next) {
-        gchar **value_array =
-            g_strsplit ((gchar *) g_hash_table_lookup (effect_table,
-                (gchar *) tmp_key->data),
-            (gchar *) ")", (gint) 0);
-        gchar *value = g_ascii_strdown (value_array[1], -1);
+  for (tmp = list; tmp; tmp = tmp->next) {
 
-        if (!g_strcmp0 (value, (gchar *) "true"))
-          ges_track_object_set_child_property (GES_TRACK_OBJECT (effect),
-              (gchar *) tmp_key->data, TRUE, NULL);
-        else if (!g_strcmp0 (value, (gchar *) "false"))
-          ges_track_object_set_child_property (GES_TRACK_OBJECT (effect),
-              (gchar *) tmp_key->data, FALSE, NULL);
-        else if (!g_strcmp0 (value, (gchar *) "effect"))
-          continue;
-        else if (!g_strcmp0 ((gchar *) "(guint", value_array[0])
-            || !g_strcmp0 ((gchar *) "(GEnum", value_array[0])
-            || !g_strcmp0 ((gchar *) "(gint", value_array[0]))
-          ges_track_object_set_child_property (GES_TRACK_OBJECT (effect),
-              (gchar *) tmp_key->data, g_ascii_strtoll (value, NULL, 0), NULL);
-        else
-          ges_track_object_set_child_property (GES_TRACK_OBJECT (effect),
-              (gchar *) tmp_key->data, g_ascii_strtod (value, NULL), NULL);
+    SrcMapping *srcmap = (SrcMapping *) tmp->data;
+
+    xmlTextWriterStartElement (writer, BAD_CAST "timeline-object");
+    xmlTextWriterStartElement (writer, BAD_CAST "factory-ref");
+    xmlTextWriterWriteAttribute (writer, BAD_CAST "id", BAD_CAST srcmap->id);
+    xmlTextWriterEndElement (writer);
+    xmlTextWriterStartElement (writer, BAD_CAST "track-object-refs");
+
+    for (tck_obj_ids = srcmap->tck_obj_ids; tck_obj_ids;
+        tck_obj_ids = tck_obj_ids->next) {
+      xmlTextWriterStartElement (writer, BAD_CAST "track-object-ref");
+      xmlTextWriterWriteAttribute (writer, BAD_CAST "id",
+          BAD_CAST tck_obj_ids->data);
+      xmlTextWriterEndElement (writer);
+    }
+    xmlTextWriterEndElement (writer);
+    xmlTextWriterEndElement (writer);
+  }
+  xmlTextWriterEndElement (writer);
+}
+
+static gboolean
+save_pitivi_timeline_to_uri (GESFormatter * pitivi_formatter,
+    GESTimeline * timeline, const gchar * uri)
+{
+  xmlTextWriterPtr writer;
+  GList *list = NULL, *layers = NULL;
+
+  writer = xmlNewTextWriterFilename (uri, 0);
+  xmlTextWriterSetIndent (writer, 1);
+  xmlTextWriterStartElement (writer, BAD_CAST "pitivi");
+  xmlTextWriterWriteAttribute (writer, BAD_CAST "formatter", BAD_CAST "GES");
+  /*  */
+  xmlTextWriterWriteAttribute (writer, BAD_CAST "version", BAD_CAST VERSION);
+
+  xmlTextWriterStartElement (writer, BAD_CAST "factories");
+  xmlTextWriterStartElement (writer, BAD_CAST "sources");
+
+  layers = ges_timeline_get_layers (timeline);
+  list = save_sources (layers, writer);
+
+  xmlTextWriterEndElement (writer);
+  xmlTextWriterEndElement (writer);
+
+  save_tracks (timeline, writer, list);
+  save_timeline_objects (writer, list);
+  xmlTextWriterEndDocument (writer);
+  xmlFreeTextWriter (writer);
+
+  g_list_free (layers);
+  g_list_foreach (list, (GFunc) free_src_map, NULL);
+  g_list_free (list);
+
+  return TRUE;
+}
+
+/* Project loading functions */
+
+/* Return: a GHashTable containing:
+ *    {attr: value}
+ */
+static GHashTable *
+get_nodes_infos (xmlNodePtr node)
+{
+  xmlAttr *cur_attr;
+  GHashTable *props_table;
+  gchar *name, *value;
+
+  props_table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
+
+  for (cur_attr = node->properties; cur_attr; cur_attr = cur_attr->next) {
+    name = (gchar *) cur_attr->name;
+    value = (gchar *) xmlGetProp (node, cur_attr->name);
+    g_hash_table_insert (props_table, g_strdup (name), g_strdup (value));
+    xmlFree (value);
+  }
+
+  return props_table;
+}
+
+static gboolean
+create_tracks (GESFormatter * self)
+{
+  GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
+  GList *tracks = NULL;
+
+  tracks = ges_timeline_get_tracks (priv->timeline);
+
+  GST_DEBUG ("Creating tracks, current number of tracks",
+      g_list_length (tracks));
+
+  if (tracks) {
+    GList *tmp = NULL;
+    GESTrack *track;
+    for (tmp = tracks; tmp; tmp = tmp->next) {
+      track = tmp->data;
+      if (track->type == GES_TRACK_TYPE_AUDIO) {
+        priv->tracka = track;
+      } else {
+        priv->trackv = track;
       }
     }
+    g_list_free_full (tracks, g_object_unref);
+    return TRUE;
   }
 
-  if (a_avail) {
-    ges_timeline_filesource_set_supported_formats (src, GES_TRACK_TYPE_VIDEO);
-  } else if (v_avail) {
-    ges_timeline_filesource_set_supported_formats (src, GES_TRACK_TYPE_AUDIO);
+  priv->tracka = ges_track_audio_raw_new ();
+  priv->trackv = ges_track_video_raw_new ();
+
+  if (!ges_timeline_add_track (priv->timeline, priv->trackv)) {
+    return FALSE;
   }
-  free (prio);
+
+  if (!ges_timeline_add_track (priv->timeline, priv->tracka)) {
+    return FALSE;
+  }
+
+  return TRUE;
 }
 
-void
-set_properties (GObject * obj, GHashTable * props_table)
+static GHashTable *
+list_sources (GESFormatter * self)
 {
-  gint i;
-  gchar **prop_array;
-  gint64 prop_value;
+  GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
+  xmlXPathObjectPtr xpathObj;
+  GHashTable *table, *sources_table;
+  int size, j;
+  gchar *id;
+  xmlNodeSetPtr nodes;
 
-  gchar list[3][10] = { "duration", "in_point", "start" };
+  sources_table =
+      g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
+      (GDestroyNotify) g_hash_table_destroy);
 
-  for (i = 0; i < 3; i++) {
-    prop_array =
-        g_strsplit ((gchar *) g_hash_table_lookup (props_table, list[i]),
-        (gchar *) ")", (gint) 0);
-    prop_value = g_ascii_strtoll ((gchar *) prop_array[1], NULL, 0);
-    g_object_set (obj, list[i], prop_value, NULL);
-    g_strfreev (prop_array);
+  xpathObj = xmlXPathEvalExpression ((const xmlChar *)
+      "/pitivi/factories/sources/source", priv->xpathCtx);
+  nodes = xpathObj->nodesetval;
+
+  size = (nodes) ? nodes->nodeNr : 0;
+  for (j = 0; j < size; ++j) {
+    table = get_nodes_infos (nodes->nodeTab[j]);
+    id = (gchar *) g_hash_table_lookup (table, (gchar *) "id");
+    g_hash_table_insert (sources_table, g_strdup (id), table);
   }
+
+  xmlXPathFreeObject (xpathObj);
+  return sources_table;
 }
 
 static gboolean
@@ -749,60 +562,75 @@ parse_track_objects (GESFormatter * self)
   xmlNodeSetPtr nodes;
   int size, j;
   gchar *id, *fac_ref;
-  GHashTable *table = NULL, *new_table, *effect_table;
-  xmlNode *ref_node;
+  GHashTable *table = NULL, *effect_table = NULL;
+  xmlNode *first_child;
   gchar *media_type;
 
+  /* FIXME Make this whole function cleaner starting from
+   * "/pitivi/timeline/tracks/track/stream" and descending
+   * into the children. */
   xpathObj = xmlXPathEvalExpression ((const xmlChar *)
       "/pitivi/timeline/tracks/track/track-objects/track-object",
       priv->xpathCtx);
 
   if (xpathObj == NULL) {
-    xmlXPathFreeObject (xpathObj);
+    GST_DEBUG ("No track object found");
+
     return FALSE;
   }
+
   nodes = xpathObj->nodesetval;
   size = (nodes) ? nodes->nodeNr : 0;
 
   for (j = 0; j < size; ++j) {
-    GHashTable *new_effect_table = NULL;
+    xmlNodePtr node = nodes->nodeTab[j];
+
     table = get_nodes_infos (nodes->nodeTab[j]);
     id = (gchar *) g_hash_table_lookup (table, (gchar *) "id");
-    ref_node = nodes->nodeTab[j]->children->next;
-    fac_ref = (gchar *) xmlGetProp (ref_node, (xmlChar *) "id");
-    new_table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
-
-    if (!g_strcmp0 ((gchar *) ref_node->name, (gchar *) "effect")) {
-      fac_ref = (gchar *) "effect";
-      ref_node = ref_node->children->next;
-      new_effect_table =
-          g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
+    first_child = nodes->nodeTab[j]->children->next;
+    fac_ref = (gchar *) xmlGetProp (first_child, (xmlChar *) "id");
+
+    /* We check if the first child is "effect" */
+    if (!g_strcmp0 ((gchar *) first_child->name, (gchar *) "effect")) {
+      xmlChar *effect_name;
+      xmlNodePtr fact_node = first_child->children->next;
+
+      /* We have a node called "text" in between thus ->next->next */
+      xmlNodePtr elem_props_node = fact_node->next->next;
+
+      effect_name = xmlGetProp (fact_node, (xmlChar *) "name");
       g_hash_table_insert (table, g_strdup ((gchar *) "effect_name"),
-          g_strdup ((gchar *) xmlGetProp (ref_node, (xmlChar *) "name")));
-      effect_table = get_nodes_infos (ref_node->next->next);
-      g_hash_table_foreach (effect_table, (GHFunc) create_new_source_table,
-          new_effect_table);
+          g_strdup ((gchar *) effect_name));
+      xmlFree (effect_name);
+
+      /* We put the effects properties in an hacktable (Lapsus is on :) */
+      effect_table = get_nodes_infos (elem_props_node);
+
+      g_hash_table_insert (table, g_strdup ((gchar *) "fac_ref"),
+          g_strdup ("effect"));
+
+      xmlFree (fac_ref);
+    } else {
+
+      g_hash_table_insert (table, g_strdup ((gchar *) "fac_ref"),
+          g_strdup (fac_ref));
+      xmlFree (fac_ref);
     }
 
-    g_hash_table_insert (table, g_strdup ((gchar *) "fac_ref"),
-        g_strdup (fac_ref));
-    media_type =
-        (gchar *) xmlGetProp (nodes->nodeTab[j]->parent->prev->prev,
-        (xmlChar *) "type");
+    /* Same as before, we got a text node in between, thus the 2 prev
+     * node->parent is <track-objects>, the one before is <stream>
+     */
+    media_type = (gchar *) xmlGetProp (node->parent->prev->prev,
+        (const xmlChar *) "type");
     g_hash_table_insert (table, g_strdup ((gchar *) "media_type"),
         g_strdup (media_type));
-    g_hash_table_foreach (table, (GHFunc) create_new_source_table, new_table);
-    if (new_effect_table) {
-      g_hash_table_insert (new_table, (gchar *) "effect_props",
-          new_effect_table);
-    }
-    g_hash_table_insert (priv->track_objects_table, g_strdup (id), new_table);
     xmlFree (media_type);
-    if (g_strcmp0 (fac_ref, (gchar *) "effect")) {
-      xmlFree (fac_ref);
-    }
-    g_hash_table_foreach (table, (GHFunc) destroyer, NULL);
-    g_hash_table_destroy (table);
+
+
+    if (effect_table)
+      g_hash_table_insert (table, g_strdup ("effect_props"), effect_table);
+
+    g_hash_table_insert (priv->track_objects_table, g_strdup (id), table);
   }
 
   xmlXPathFreeObject (xpathObj);
@@ -812,18 +640,18 @@ parse_track_objects (GESFormatter * self)
 static gboolean
 parse_timeline_objects (GESFormatter * self)
 {
-  GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
-  xmlXPathObjectPtr xpathObj;
-  xmlNodeSetPtr nodes;
   int size, j;
-  gchar *id, *ref;
-  xmlChar *refxml;
+  xmlNodeSetPtr nodes;
+  xmlXPathObjectPtr xpathObj;
+  xmlNodePtr tlobj_nd, tmp_nd, tmp_nd2;
+  xmlChar *tckobjrefId, *facrefId = NULL;
+
+  GList *reflist = NULL;
+  GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
+  GHashTable *tlobjs_table = priv->timeline_objects_table;
 
-  GList *ref_list = NULL, *tmp_list = NULL, *tmp = NULL;
-  xmlNode *cur_node = NULL;
   xpathObj = xmlXPathEvalExpression ((const xmlChar *)
-      "/pitivi/timeline/timeline-objects/timeline-object/factory-ref",
-      priv->xpathCtx);
+      "/pitivi/timeline/timeline-objects/timeline-object", priv->xpathCtx);
 
   if (xpathObj == NULL) {
     xmlXPathFreeObject (xpathObj);
@@ -833,195 +661,356 @@ parse_timeline_objects (GESFormatter * self)
   nodes = xpathObj->nodesetval;
   size = (nodes) ? nodes->nodeNr : 0;
 
-  for (j = 0; j < size; ++j) {
-    cur_node = nodes->nodeTab[j];
-    id = (gchar *) xmlGetProp (cur_node, (xmlChar *) "id");
-    cur_node = cur_node->next->next->children->next;
-    ref_list = NULL;
-    for (cur_node = cur_node; cur_node; cur_node = cur_node->next->next) {
-
-      refxml = xmlGetProp (cur_node, (xmlChar *) "id");
-      ref = (gchar *) refxml;
-      ref_list = g_list_append (ref_list, g_strdup (ref));
-      xmlFree (refxml);
-    }
-    tmp_list = g_hash_table_lookup (priv->timeline_objects_table, id);
-    if (tmp_list != NULL) {
-      for (tmp = tmp_list; tmp; tmp = tmp->next) {
-        ref_list = g_list_append (ref_list, tmp->data);
-      }
-    }
-    g_hash_table_insert (priv->timeline_objects_table, g_strdup (id),
-        g_list_copy (ref_list));
-    xmlFree (id);
-    g_list_free (ref_list);
-    g_list_free (tmp_list);
-    g_list_free (tmp);
-  }
-  xmlXPathFreeObject (xpathObj);
-  return TRUE;
-}
+  for (j = 0; j < size; j++) {
+    tlobj_nd = nodes->nodeTab[j];
 
-static void
-create_new_source_table (gchar * key, gchar * value, GHashTable * table)
-{
-  g_hash_table_insert (table, g_strdup (key), g_strdup (value));
-}
+    for (tmp_nd = tlobj_nd->children; tmp_nd; tmp_nd = tmp_nd->next) {
+      /* We assume that factory-ref is always before the tckobjs-ref */
+      if (!xmlStrcmp (tmp_nd->name, (xmlChar *) "factory-ref")) {
+        facrefId = xmlGetProp (tmp_nd, (xmlChar *) "id");
 
-static gboolean
-create_tracks (GESFormatter * self)
-{
-  GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
-  GList *tracks = NULL;
+      } else if (!xmlStrcmp (tmp_nd->name, (xmlChar *) "track-object-refs")) {
 
-  tracks = ges_timeline_get_tracks (priv->timeline);
-  if (g_list_length (tracks)) {
-    GList *tmp = NULL;
-    GESTrack *track;
-    for (tmp = tracks; tmp; tmp = tmp->next) {
-      track = tmp->data;
-      if (track->type == GES_TRACK_TYPE_AUDIO) {
-        priv->tracka = track;
-      } else {
-        priv->trackv = track;
+        for (tmp_nd2 = tmp_nd->children; tmp_nd2; tmp_nd2 = tmp_nd2->next) {
+          if (!xmlStrcmp (tmp_nd2->name, (xmlChar *) "track-object-ref")) {
+            /* We add the track object ref ID to the list of the current
+             * TimelineObject tracks, this way we can merge 2
+             * TimelineObject-s into 1 when we have unlinked TrackObject-s */
+            reflist = g_hash_table_lookup (tlobjs_table, facrefId);
+            tckobjrefId = xmlGetProp (tmp_nd2, (xmlChar *) "id");
+            reflist = g_list_append (reflist, g_strdup ((gchar *) tckobjrefId));
+            g_hash_table_insert (tlobjs_table, g_strdup ((gchar *) facrefId),
+                reflist);
+
+            xmlFree (tckobjrefId);
+          }
+        }
       }
     }
-    g_list_free (tracks);
-    return TRUE;
-  }
-
-  priv->tracka = ges_track_audio_raw_new ();
-  priv->trackv = ges_track_video_raw_new ();
-
-  if (!ges_timeline_add_track (priv->timeline, priv->trackv)) {
-    return FALSE;
-  }
-
-  if (!ges_timeline_add_track (priv->timeline, priv->tracka)) {
-    return FALSE;
   }
 
+  xmlXPathFreeObject (xpathObj);
   return TRUE;
 }
 
-static GHashTable *
-get_nodes_infos (xmlNodePtr node)
+static void
+set_properties (GObject * obj, GHashTable * props_table)
 {
-  xmlAttr *cur_attr;
-  GHashTable *props_table;
-  gchar *name, *value;
-
-  props_table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
-
-  for (cur_attr = node->properties; cur_attr; cur_attr = cur_attr->next) {
-    name = (gchar *) cur_attr->name;
-    value = (gchar *) xmlGetProp (node, cur_attr->name);
-    g_hash_table_insert (props_table, g_strdup (name), g_strdup (value));
-    xmlFree (value);
-  }
+  gint i;
+  gchar **prop_array, *valuestr;
+  gint64 value;
 
-  return props_table;
-}
+  gchar props[3][10] = { "duration", "in_point", "start" };
 
-static void
-destroy_all (GList * list)
-{
-  g_free (g_list_nth (list, (guint) 2)->data);
-  g_object_unref (G_OBJECT (g_list_nth (list, (guint) 1)->data));
-  g_free (g_list_nth (list, (guint) 3)->data);
+  for (i = 0; i < 3; i++) {
+    valuestr = g_hash_table_lookup (props_table, props[i]);
+    prop_array = g_strsplit (valuestr, ")", 0);
+    value = g_ascii_strtoll (prop_array[1], NULL, 0);
+    g_object_set (obj, props[i], value, NULL);
 
-  if (g_list_length (list) == 6) {
-    g_free (g_list_nth (list, (guint) 5)->data);
+    g_strfreev (prop_array);
   }
-
-  g_free (g_list_nth (list, (guint) 0)->data);
-  g_list_free (list);
 }
 
 static void
 track_object_added_cb (GESTimelineObject * object,
     GESTrackObject * track_object, GHashTable * props_table)
 {
-  gchar *media_type = NULL;
+  gchar *media_type = NULL, *lockedstr;
   GList *tck_objs = NULL, *tmp = NULL;
-  GESTrack *object_track;
+  GESTrack *track;
   gint64 start, duration;
-  gboolean has_effect = FALSE;
+  gboolean has_effect = FALSE, locked = TRUE;
   gint type = 0;
+
   tck_objs = ges_timeline_object_get_track_objects (object);
-  media_type =
-      (gchar *) g_hash_table_lookup (props_table, (gchar *) "media_type");
+  media_type = (gchar *) g_hash_table_lookup (props_table, "media_type");
+  lockedstr = (gchar *) g_hash_table_lookup (props_table, "locked");
+
+  if (lockedstr && !g_strcmp0 (lockedstr, "(bool)False"))
+    locked = FALSE;
 
   for (tmp = tck_objs; tmp; tmp = tmp->next) {
-    object_track = ges_track_object_get_track (tmp->data);
+
+    if (!GES_IS_TRACK_OBJECT (tmp->data)) {
+      /* If we arrive here something massively screwed */
+      GST_ERROR ("Not a TrackObject, this is a bug");
+      continue;
+    }
+
+    track = ges_track_object_get_track (tmp->data);
+    if (!track) {
+      GST_WARNING ("TrackObject not in a track yet");
+      continue;
+    }
+
     if (GES_IS_TRACK_PARSE_LAUNCH_EFFECT (tmp->data)) {
       has_effect = TRUE;
       continue;
     }
+
     if ((!g_strcmp0 (media_type, "pitivi.stream.VideoStream")
-            && object_track->type == GES_TRACK_TYPE_VIDEO)
+            && track->type == GES_TRACK_TYPE_VIDEO)
         || (!g_strcmp0 (media_type, "pitivi.stream.AudioStream")
-            && object_track->type == GES_TRACK_TYPE_AUDIO)) {
+            && track->type == GES_TRACK_TYPE_AUDIO)) {
+
+      /* We lock the track objects so we do not move the whole TimelineObject */
       ges_track_object_set_locked (tmp->data, FALSE);
       set_properties (G_OBJECT (tmp->data), props_table);
-      ges_track_object_set_locked (tmp->data, TRUE);
-      type = object_track->type;
+
+      if (locked)
+        ges_track_object_set_locked (tmp->data, TRUE);
+
+      type = track->type;
       g_object_get (tmp->data, "start", &start, "duration", &duration, NULL);
     }
   }
 
   if (has_effect) {
     tck_objs = ges_timeline_object_get_track_objects (object);
+
+    /* FIXME make sure this is te way we want to handle that
+     * ie: set duration and start as the other trackobject
+     * and no let full control to the user. */
+
     for (tmp = tck_objs; tmp; tmp = tmp->next) {
-      object_track = ges_track_object_get_track (tmp->data);
+      /* We set the effects start and duration */
+      track = ges_track_object_get_track (tmp->data);
+
       if (GES_IS_TRACK_PARSE_LAUNCH_EFFECT (tmp->data)
-          && (type == object_track->type)) {
+          && (type == track->type)) {
+        /* We lock the track objects so we do not move the whole TimelineObject */
         ges_track_object_set_locked (tmp->data, FALSE);
         g_object_set (tmp->data, "start", start, "duration", duration, NULL);
-        ges_track_object_set_locked (tmp->data, TRUE);
+        if (locked)
+          ges_track_object_set_locked (tmp->data, TRUE);
       }
     }
   }
-}
 
-void
-ultimate_table_destroyer (gpointer data, gpointer data2, void *unused)
-{
-  g_free (data);
-  g_hash_table_foreach (data2, (GHFunc) destroyer, NULL);
-  g_hash_table_destroy (data2);
 }
 
-void
-layers_table_destroyer (gpointer data, gpointer data2, void *unused)
+static void
+make_source (GESFormatter * self, GList * reflist, GHashTable * source_table)
 {
-  g_object_unref (data2);
-  g_free (data);
-}
+  GHashTable *props_table, *effect_table;
+  gchar **prio_array;
+  GESTimelineLayer *layer;
+  GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
 
-void
-list_table_destroyer (gpointer data, gpointer data2, void *unused)
-{
-  g_list_foreach (data2, (GFunc) g_free, NULL);
-  g_list_free (data2);
-  g_free (data);
+  gchar *fac_ref = NULL, *media_type = NULL, *filename = NULL, *prio_str;
+  GList *tmp = NULL, *keys, *tmp_key;
+  GESTimelineFileSource *src = NULL;
+  gint prio;
+  gboolean a_avail = FALSE, v_avail = FALSE, video;
+  GHashTable *tckobj_table = priv->track_objects_table;
+
+  for (tmp = reflist; tmp; tmp = tmp->next) {
+
+    /* Get the layer */
+    props_table = g_hash_table_lookup (tckobj_table, (gchar *) tmp->data);
+    prio_str = (gchar *) g_hash_table_lookup (props_table, "priority");
+    prio_array = g_strsplit (prio_str, ")", 0);
+    prio = (gint) g_ascii_strtod (prio_array[1], NULL);
+    g_strfreev (prio_array);
+
+    /* If we do not have any layer with this priority, create it */
+    if (!(layer = g_hash_table_lookup (priv->layers_table, &prio))) {
+      layer = ges_timeline_layer_new ();
+      g_object_set (layer, "auto-transition", TRUE, "priority", prio, NULL);
+      ges_timeline_add_layer (priv->timeline, layer);
+      g_hash_table_insert (priv->layers_table, g_memdup (&prio,
+              sizeof (guint64)), layer);
+    }
+
+    fac_ref = (gchar *) g_hash_table_lookup (props_table, "fac_ref");
+    media_type = (gchar *) g_hash_table_lookup (props_table, "media_type");
+
+    if (!g_strcmp0 (media_type, "pitivi.stream.VideoStream"))
+      video = TRUE;
+    else
+      video = FALSE;
+
+    /* FIXME I am sure we could reimplement this whole part
+     * in a simpler way */
+    if (g_strcmp0 (fac_ref, (gchar *) "effect")) {
+      if (a_avail && (!video)) {
+        a_avail = FALSE;
+        g_signal_connect (src, "track-object-added",
+            G_CALLBACK (track_object_added_cb), props_table);
+      } else if (v_avail && (video)) {
+        v_avail = FALSE;
+        g_signal_connect (src, "track-object-added",
+            G_CALLBACK (track_object_added_cb), props_table);
+
+      } else {
+
+        /* If we only have audio or only video in the previous source,
+         * set it has such */
+        if (a_avail) {
+          ges_timeline_filesource_set_supported_formats (src,
+              GES_TRACK_TYPE_VIDEO);
+        } else if (v_avail) {
+          ges_timeline_filesource_set_supported_formats (src,
+              GES_TRACK_TYPE_AUDIO);
+        }
+
+        filename = (gchar *) g_hash_table_lookup (source_table, "filename");
+
+        src = ges_timeline_filesource_new (filename);
+
+        if (!video) {
+          v_avail = TRUE;
+          a_avail = FALSE;
+        } else {
+          a_avail = TRUE;
+          v_avail = FALSE;
+        }
+        set_properties (G_OBJECT (src), props_table);
+        ges_timeline_layer_add_object (layer, GES_TIMELINE_OBJECT (src));
+      }
+
+    } else {
+      GESTrackParseLaunchEffect *effect;
+      gchar *active = (gchar *) g_hash_table_lookup (props_table, "active");
+
+      effect = ges_track_parse_launch_effect_new ((gchar *)
+          g_hash_table_lookup (props_table, (gchar *) "effect_name"));
+      effect_table =
+          g_hash_table_lookup (props_table, (gchar *) "effect_props");
+
+      ges_timeline_object_add_track_object (GES_TIMELINE_OBJECT (src),
+          GES_TRACK_OBJECT (effect));
+
+      if (!g_strcmp0 (active, (gchar *) "(bool)False"))
+        ges_track_object_set_active (GES_TRACK_OBJECT (effect), FALSE);
+
+      if (video)
+        ges_track_add_object (priv->trackv, GES_TRACK_OBJECT (effect));
+      else
+        ges_track_add_object (priv->tracka, GES_TRACK_OBJECT (effect));
+
+      /* Set effect properties */
+      keys = g_hash_table_get_keys (effect_table);
+      for (tmp_key = keys; tmp_key; tmp_key = tmp_key->next) {
+        GstStructure *structure;
+        const GValue *value;
+        GParamSpec *spec;
+        GstCaps *caps;
+        gchar *prop_val;
+
+        prop_val = (gchar *) g_hash_table_lookup (effect_table,
+            (gchar *) tmp_key->data);
+
+        if (g_strstr_len (prop_val, -1, "(GEnum)")) {
+          gchar **val = g_strsplit (prop_val, ")", 2);
+
+          ges_track_object_set_child_property (GES_TRACK_OBJECT (effect),
+              (gchar *) tmp_key->data, atoi (val[1]), NULL);
+          g_strfreev (val);
+
+        } else if (ges_track_object_lookup_child (GES_TRACK_OBJECT (effect),
+                (gchar *) tmp->data, NULL, &spec)) {
+          gchar *caps_str = g_strdup_printf ("structure1, property1=%s;",
+              prop_val);
+
+          caps = gst_caps_from_string (caps_str);
+          g_free (caps_str);
+          structure = gst_caps_get_structure (caps, 0);
+          value = gst_structure_get_value (structure, "property1");
+
+          ges_track_object_set_child_property_by_pspec (GES_TRACK_OBJECT
+              (effect), spec, (GValue *) value);
+          gst_caps_unref (caps);
+        }
+      }
+    }
+  }
+
+  if (a_avail) {
+    ges_timeline_filesource_set_supported_formats (src, GES_TRACK_TYPE_VIDEO);
+  } else if (v_avail) {
+    ges_timeline_filesource_set_supported_formats (src, GES_TRACK_TYPE_AUDIO);
+  }
 }
 
-void
-destroyer (gpointer data, gpointer data2, void *unused)
+static gboolean
+make_timeline_objects (GESFormatter * self)
 {
-  if (!g_strcmp0 ((gchar *) data, (gchar *) "private")) {
-  } else if (!g_strcmp0 ((gchar *) data, (gchar *) "effect_props")) {
-    g_hash_table_foreach (data2, (GHFunc) destroyer, NULL);
-    g_hash_table_destroy (data2);
-  } else {
-    g_free (data2);
-    g_free (data);
+  GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
+  GHashTable *source_table;
+
+  GList *keys = NULL, *tmp = NULL, *reflist = NULL;
+
+  keys = g_hash_table_get_keys (priv->timeline_objects_table);
+
+  for (tmp = keys; tmp; tmp = tmp->next) {
+    gchar *fac_id = (gchar *) tmp->data;
+
+    reflist = g_hash_table_lookup (priv->timeline_objects_table, fac_id);
+    source_table = g_hash_table_lookup (priv->source_table, fac_id);
+    make_source (self, reflist, source_table);
   }
+
+  g_list_free (keys);
+  return TRUE;
 }
 
-GESPitiviFormatter *
-ges_pitivi_formatter_new (void)
+static gboolean
+load_pitivi_file_from_uri (GESFormatter * self,
+    GESTimeline * timeline, const gchar * uri)
 {
-  return g_object_new (GES_TYPE_PITIVI_FORMATTER, NULL);
+  xmlDocPtr doc;
+  GESTimelineLayer *layer;
+  GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
+
+  gboolean ret = TRUE;
+  gint *prio = malloc (sizeof (gint));
+
+  *prio = 0;
+  layer = ges_timeline_layer_new ();
+  g_object_set (layer, "auto-transition", TRUE, NULL);
+
+  g_hash_table_insert (priv->layers_table, prio, layer);
+  priv->timeline = timeline;
+  g_object_set (layer, "priority", (gint32) 0, NULL);
+
+  if (!ges_timeline_add_layer (timeline, layer)) {
+    GST_ERROR ("Couldn't add layer");
+    return FALSE;
+  }
+
+  if (!(doc = xmlParseFile (uri))) {
+    GST_ERROR ("The xptv file for uri %s was badly formed or did not exist",
+        uri);
+    return FALSE;
+  }
+
+  priv->xpathCtx = xmlXPathNewContext (doc);
+
+  if (!create_tracks (self)) {
+    GST_ERROR ("Couldn't create tracks");
+    return FALSE;
+  }
+
+  priv->source_table = list_sources (self);
+
+  if (!parse_timeline_objects (self)) {
+    GST_ERROR ("Couldn't find timeline objects markup in the xptv file");
+    return FALSE;
+  }
+
+  if (!parse_track_objects (self)) {
+    GST_ERROR ("Couldn't find track objects markup in the xptv file");
+    return FALSE;
+  }
+
+  if (!make_timeline_objects (self)) {
+    GST_ERROR ("Couldn't deserialise the project properly");
+    return FALSE;
+  }
+
+  xmlXPathFreeContext (priv->xpathCtx);
+  xmlFreeDoc (doc);
+  return ret;
 }