command-line-formatter: Stop uselessly looping over options
[platform/upstream/gstreamer.git] / ges / ges-pitivi-formatter.c
1 /* GStreamer Editing Services Pitivi Formatter
2  * Copyright (C) 2011-2012 Mathieu Duponchelle <seeed@laposte.net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 /**
21  * SECTION: gespitiviformatter
22  * @title: GESPitiviFormatter
23  * @short_description: A formatter for the obsolete Pitivi xptv project file format
24  *
25  * This is a legacy format and you should avoid to use it. The formatter
26  * is really not in good shape and is deprecated.
27  *
28  * Deprecated: 1.0
29  */
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #undef VERSION
33 #endif
34
35 #include <libxml/xmlreader.h>
36 #include <libxml/tree.h>
37 #include <libxml/parser.h>
38 #include <libxml/xpath.h>
39 #include <libxml/xpathInternals.h>
40 #include <libxml/encoding.h>
41 #include <libxml/xmlwriter.h>
42
43 #include "ges-internal.h"
44 #include <ges/ges-pitivi-formatter.h>
45 #include <ges/ges.h>
46
47 /* The Pitivi etree formatter is 0.1 we set GES one to 0.2 */
48 //#define VERSION "0.2"
49 #define DOUBLE_VERSION 0.2
50
51 #undef GST_CAT_DEFAULT
52 GST_DEBUG_CATEGORY_STATIC (ges_pitivi_formatter_debug);
53 #define GST_CAT_DEFAULT ges_pitivi_formatter_debug
54
55 typedef struct SrcMapping
56 {
57   gchar *id;
58   GESClip *clip;
59   guint priority;
60   GList *track_element_ids;
61 } SrcMapping;
62
63 struct _GESPitiviFormatterPrivate
64 {
65   xmlXPathContextPtr xpathCtx;
66
67   /* {"sourceId" : {"prop": "value"}} */
68   GHashTable *sources_table;
69
70   /* Used as a set of the uris */
71   GHashTable *source_uris;
72
73   /* {trackId: {"factory_ref": factoryId, ""}
74    * if effect:
75    *      {"factory_ref": "effect",
76    *       "effect_name": name
77    *       "effect_props": {"propname": value}}}
78    */
79   GHashTable *track_elements_table;
80
81   /* {factory-ref: [track-object-ref-id,...]} */
82   GHashTable *clips_table;
83
84   /* {layerPriority: layer} */
85   GHashTable *layers_table;
86
87   GESTimeline *timeline;
88
89   GESTrack *tracka, *trackv;
90
91   /* List the Clip that haven't been loaded yet */
92   GList *sources_to_load;
93
94   /* Saving context */
95   /* {factory_id: uri} */
96   GHashTable *saving_source_table;
97   guint nb_sources;
98 };
99
100 G_DEFINE_TYPE_WITH_PRIVATE (GESPitiviFormatter, ges_pitivi_formatter,
101     GES_TYPE_FORMATTER);
102
103
104 static void
105 list_table_destroyer (gpointer key, gpointer value, void *unused)
106 {
107   g_list_foreach (value, (GFunc) g_free, NULL);
108   g_list_free (value);
109 }
110
111 static gboolean
112 pitivi_can_load_uri (GESFormatter * dummy_instance, const gchar * uri,
113     GError ** error)
114 {
115   xmlDocPtr doc;
116   gboolean ret = TRUE;
117   xmlXPathObjectPtr xpathObj;
118   xmlXPathContextPtr xpathCtx;
119   gchar *filename = g_filename_from_uri (uri, NULL, NULL);
120
121   if (!filename || !g_file_test (filename, G_FILE_TEST_EXISTS)) {
122     g_free (filename);
123     return FALSE;
124   }
125
126   g_free (filename);
127
128   if (!(doc = xmlParseFile (uri))) {
129     GST_ERROR ("The xptv file for uri %s was badly formed", uri);
130     return FALSE;
131   }
132
133   xpathCtx = xmlXPathNewContext (doc);
134   xpathObj = xmlXPathEvalExpression ((const xmlChar *) "/pitivi", xpathCtx);
135   if (!xpathObj || !xpathObj->nodesetval || xpathObj->nodesetval->nodeNr == 0)
136     ret = FALSE;
137
138   xmlFreeDoc (doc);
139   xmlXPathFreeObject (xpathObj);
140   xmlXPathFreeContext (xpathCtx);
141
142   return ret;
143 }
144
145 /* Project loading functions */
146
147 /* Return: a GHashTable containing:
148  *    {attr: value}
149  */
150 static GHashTable *
151 get_nodes_infos (xmlNodePtr node)
152 {
153   xmlAttr *cur_attr;
154   GHashTable *props_table;
155   gchar *name, *value;
156
157   props_table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
158
159   for (cur_attr = node->properties; cur_attr; cur_attr = cur_attr->next) {
160     name = (gchar *) cur_attr->name;
161     value = (gchar *) xmlGetProp (node, cur_attr->name);
162     g_hash_table_insert (props_table, g_strdup (name), g_strdup (value));
163     xmlFree (value);
164   }
165
166   return props_table;
167 }
168
169 static gboolean
170 create_tracks (GESFormatter * self)
171 {
172   GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
173   GList *tracks = NULL;
174
175   tracks = ges_timeline_get_tracks (self->timeline);
176
177   GST_DEBUG ("Creating tracks, current number of tracks %d",
178       g_list_length (tracks));
179
180   if (tracks) {
181     GList *tmp = NULL;
182     GESTrack *track;
183     for (tmp = tracks; tmp; tmp = tmp->next) {
184       track = tmp->data;
185       if (track->type == GES_TRACK_TYPE_AUDIO) {
186         priv->tracka = track;
187       } else {
188         priv->trackv = track;
189       }
190     }
191     g_list_foreach (tracks, (GFunc) gst_object_unref, NULL);
192     g_list_free (tracks);
193     return TRUE;
194   }
195
196   priv->tracka = GES_TRACK (ges_audio_track_new ());
197   priv->trackv = GES_TRACK (ges_video_track_new ());
198
199   if (!ges_timeline_add_track (self->timeline, priv->trackv)) {
200     return FALSE;
201   }
202
203   if (!ges_timeline_add_track (self->timeline, priv->tracka)) {
204     return FALSE;
205   }
206
207   return TRUE;
208 }
209
210 static void
211 parse_metadatas (GESFormatter * self)
212 {
213   guint i, size;
214   xmlNodePtr node;
215   xmlAttr *cur_attr;
216   xmlNodeSetPtr nodes;
217   xmlXPathObjectPtr xpathObj;
218   GESMetaContainer *metacontainer = GES_META_CONTAINER (self->project);
219
220   xpathObj = xmlXPathEvalExpression ((const xmlChar *)
221       "/pitivi/metadata", GES_PITIVI_FORMATTER (self)->priv->xpathCtx);
222   nodes = xpathObj->nodesetval;
223
224   size = (nodes) ? nodes->nodeNr : 0;
225   for (i = 0; i < size; i++) {
226     node = nodes->nodeTab[i];
227     for (cur_attr = node->properties; cur_attr; cur_attr = cur_attr->next) {
228       ges_meta_container_set_string (metacontainer, (gchar *) cur_attr->name,
229           (gchar *) xmlGetProp (node, cur_attr->name));
230     }
231   }
232
233   xmlXPathFreeObject (xpathObj);
234 }
235
236 static void
237 list_sources (GESFormatter * self)
238 {
239   GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
240   xmlXPathObjectPtr xpathObj;
241   GHashTable *table;
242   int size, j;
243   gchar *id, *filename;
244   xmlNodeSetPtr nodes;
245
246   xpathObj = xmlXPathEvalExpression ((const xmlChar *)
247       "/pitivi/factories/sources/source", priv->xpathCtx);
248   nodes = xpathObj->nodesetval;
249
250   size = (nodes) ? nodes->nodeNr : 0;
251   for (j = 0; j < size; ++j) {
252     table = get_nodes_infos (nodes->nodeTab[j]);
253     id = (gchar *) g_hash_table_lookup (table, (gchar *) "id");
254     filename = (gchar *) g_hash_table_lookup (table, (gchar *) "filename");
255     g_hash_table_insert (priv->sources_table, g_strdup (id), table);
256     g_hash_table_insert (priv->source_uris, g_strdup (filename),
257         g_strdup (filename));
258     if (self->project)
259       ges_project_create_asset (self->project, filename, GES_TYPE_URI_CLIP);
260   }
261
262   xmlXPathFreeObject (xpathObj);
263 }
264
265 static gboolean
266 parse_track_elements (GESFormatter * self)
267 {
268   GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
269   xmlXPathObjectPtr xpathObj;
270   xmlNodeSetPtr nodes;
271   int size, j;
272   gchar *id, *fac_ref;
273   GHashTable *table = NULL, *effect_table = NULL;
274   xmlNode *first_child;
275   gchar *media_type;
276
277   /* FIXME Make this whole function cleaner starting from
278    * "/pitivi/timeline/tracks/track/stream" and descending
279    * into the children. */
280   xpathObj = xmlXPathEvalExpression ((const xmlChar *)
281       "/pitivi/timeline/tracks/track/track-objects/track-object",
282       priv->xpathCtx);
283
284   if (xpathObj == NULL) {
285     GST_DEBUG ("No track object found");
286
287     return FALSE;
288   }
289
290   nodes = xpathObj->nodesetval;
291   size = (nodes) ? nodes->nodeNr : 0;
292
293   for (j = 0; j < size; ++j) {
294     xmlNodePtr node = nodes->nodeTab[j];
295
296     table = get_nodes_infos (nodes->nodeTab[j]);
297     id = (gchar *) g_hash_table_lookup (table, (gchar *) "id");
298     first_child = nodes->nodeTab[j]->children->next;
299     fac_ref = (gchar *) xmlGetProp (first_child, (xmlChar *) "id");
300
301     /* We check if the first child is "effect" */
302     if (!g_strcmp0 ((gchar *) first_child->name, (gchar *) "effect")) {
303       xmlChar *effect_name;
304       xmlNodePtr fact_node = first_child->children->next;
305
306       /* We have a node called "text" in between thus ->next->next */
307       xmlNodePtr elem_props_node = fact_node->next->next;
308
309       effect_name = xmlGetProp (fact_node, (xmlChar *) "name");
310       g_hash_table_insert (table, g_strdup ((gchar *) "effect_name"),
311           g_strdup ((gchar *) effect_name));
312       xmlFree (effect_name);
313
314       /* We put the effects properties in an hacktable (Lapsus is on :) */
315       effect_table = get_nodes_infos (elem_props_node);
316
317       g_hash_table_insert (table, g_strdup ((gchar *) "fac_ref"),
318           g_strdup ("effect"));
319
320       xmlFree (fac_ref);
321     } else {
322
323       g_hash_table_insert (table, g_strdup ((gchar *) "fac_ref"),
324           g_strdup (fac_ref));
325       xmlFree (fac_ref);
326     }
327
328     /* Same as before, we got a text node in between, thus the 2 prev
329      * node->parent is <track-objects>, the one before is <stream>
330      */
331     media_type = (gchar *) xmlGetProp (node->parent->prev->prev,
332         (const xmlChar *) "type");
333     g_hash_table_insert (table, g_strdup ((gchar *) "media_type"),
334         g_strdup (media_type));
335     xmlFree (media_type);
336
337
338     if (effect_table)
339       g_hash_table_insert (table, g_strdup ("effect_props"), effect_table);
340
341     g_hash_table_insert (priv->track_elements_table, g_strdup (id), table);
342   }
343
344   xmlXPathFreeObject (xpathObj);
345   return TRUE;
346 }
347
348 static gboolean
349 parse_clips (GESFormatter * self)
350 {
351   int size, j;
352   xmlNodeSetPtr nodes;
353   xmlXPathObjectPtr xpathObj;
354   xmlNodePtr clip_nd, tmp_nd, tmp_nd2;
355   xmlChar *trackelementrefId, *facrefId = NULL;
356
357   GList *reflist = NULL;
358   GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
359   GHashTable *clips_table = priv->clips_table;
360
361   xpathObj = xmlXPathEvalExpression ((const xmlChar *)
362       "/pitivi/timeline/timeline-objects/timeline-object", priv->xpathCtx);
363
364   if (xpathObj == NULL) {
365     xmlXPathFreeObject (xpathObj);
366     return FALSE;
367   }
368
369   nodes = xpathObj->nodesetval;
370   size = (nodes) ? nodes->nodeNr : 0;
371
372   for (j = 0; j < size; j++) {
373     clip_nd = nodes->nodeTab[j];
374
375     for (tmp_nd = clip_nd->children; tmp_nd; tmp_nd = tmp_nd->next) {
376       /* We assume that factory-ref is always before the tckobjs-ref */
377       if (!xmlStrcmp (tmp_nd->name, (xmlChar *) "factory-ref")) {
378         facrefId = xmlGetProp (tmp_nd, (xmlChar *) "id");
379
380       } else if (!xmlStrcmp (tmp_nd->name, (xmlChar *) "track-object-refs")) {
381
382         for (tmp_nd2 = tmp_nd->children; tmp_nd2; tmp_nd2 = tmp_nd2->next) {
383           if (!xmlStrcmp (tmp_nd2->name, (xmlChar *) "track-object-ref")) {
384             /* We add the track object ref ID to the list of the current
385              * Clip tracks, this way we can merge 2
386              * Clip-s into 1 when we have unlinked TrackElement-s */
387             reflist = g_hash_table_lookup (clips_table, facrefId);
388             trackelementrefId = xmlGetProp (tmp_nd2, (xmlChar *) "id");
389             reflist =
390                 g_list_append (reflist, g_strdup ((gchar *) trackelementrefId));
391             g_hash_table_insert (clips_table, g_strdup ((gchar *) facrefId),
392                 reflist);
393
394             xmlFree (trackelementrefId);
395           }
396         }
397       }
398     }
399   }
400
401   xmlXPathFreeObject (xpathObj);
402   return TRUE;
403 }
404
405 static void
406 set_properties (GObject * obj, GHashTable * props_table)
407 {
408   gint i;
409   gchar **prop_array, *valuestr;
410   gint64 value;
411
412   gchar props[3][10] = { "duration", "in_point", "start" };
413
414   for (i = 0; i < 3; i++) {
415     valuestr = g_hash_table_lookup (props_table, props[i]);
416     prop_array = g_strsplit (valuestr, ")", 0);
417     value = g_ascii_strtoll (prop_array[1], NULL, 0);
418     g_object_set (obj, props[i], value, NULL);
419
420     g_strfreev (prop_array);
421   }
422 }
423
424 static void
425 track_element_added_cb (GESClip * clip,
426     GESTrackElement * track_element, GHashTable * props_table)
427 {
428   GESPitiviFormatter *formatter;
429
430   formatter = GES_PITIVI_FORMATTER (g_hash_table_lookup (props_table,
431           "current-formatter"));
432   if (formatter) {
433     GESPitiviFormatterPrivate *priv = formatter->priv;
434
435     /* Make sure the hack to get a ref to the formatter
436      * doesn't break everything */
437     g_hash_table_steal (props_table, "current-formatter");
438
439     priv->sources_to_load = g_list_remove (priv->sources_to_load, clip);
440     if (!priv->sources_to_load && GES_FORMATTER (formatter)->project)
441       ges_project_set_loaded (GES_FORMATTER (formatter)->project,
442           GES_FORMATTER (formatter), NULL);
443   }
444
445   /* Disconnect the signal */
446   g_signal_handlers_disconnect_by_func (clip, track_element_added_cb,
447       props_table);
448 }
449
450 static void
451 make_source (GESFormatter * self, GList * reflist, GHashTable * source_table)
452 {
453   GHashTable *props_table, *effect_table;
454   gchar **prio_array;
455   GESLayer *layer;
456   GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
457
458   gchar *fac_ref = NULL, *media_type = NULL, *filename = NULL, *prio_str;
459   GList *tmp = NULL, *keys, *tmp_key;
460   GESUriClip *src = NULL;
461   gint prio;
462   gboolean a_avail = FALSE, v_avail = FALSE, video;
463   GHashTable *trackelement_table = priv->track_elements_table;
464
465   for (tmp = reflist; tmp; tmp = tmp->next) {
466
467     /* Get the layer */
468     props_table = g_hash_table_lookup (trackelement_table, (gchar *) tmp->data);
469     prio_str = (gchar *) g_hash_table_lookup (props_table, "priority");
470     prio_array = g_strsplit (prio_str, ")", 0);
471     prio = (gint) g_ascii_strtod (prio_array[1], NULL);
472     g_strfreev (prio_array);
473
474     /* If we do not have any layer with this priority, create it */
475     if (!(layer = g_hash_table_lookup (priv->layers_table, &prio))) {
476       layer = ges_layer_new ();
477       g_object_set (layer, "auto-transition", TRUE, "priority", prio, NULL);
478       ges_timeline_add_layer (self->timeline, layer);
479       g_hash_table_insert (priv->layers_table, g_memdup (&prio,
480               sizeof (guint64)), layer);
481     }
482
483     fac_ref = (gchar *) g_hash_table_lookup (props_table, "fac_ref");
484     media_type = (gchar *) g_hash_table_lookup (props_table, "media_type");
485
486     if (!g_strcmp0 (media_type, "pitivi.stream.VideoStream"))
487       video = TRUE;
488     else
489       video = FALSE;
490
491     /* FIXME I am sure we could reimplement this whole part
492      * in a simpler way */
493
494     if (g_strcmp0 (fac_ref, (gchar *) "effect")) {
495       /* FIXME this is a hack to get a ref to the formatter when receiving
496        * child-added */
497       g_hash_table_insert (props_table, (gchar *) "current-formatter", self);
498       if (a_avail && (!video)) {
499         a_avail = FALSE;
500       } else if (v_avail && (video)) {
501         v_avail = FALSE;
502       } else {
503
504         /* If we only have audio or only video in the previous source,
505          * set it has such */
506         if (a_avail) {
507           ges_clip_set_supported_formats (GES_CLIP (src), GES_TRACK_TYPE_VIDEO);
508         } else if (v_avail) {
509           ges_clip_set_supported_formats (GES_CLIP (src), GES_TRACK_TYPE_AUDIO);
510         }
511
512         filename = (gchar *) g_hash_table_lookup (source_table, "filename");
513
514         src = ges_uri_clip_new (filename);
515
516         if (!video) {
517           v_avail = TRUE;
518           a_avail = FALSE;
519         } else {
520           a_avail = TRUE;
521           v_avail = FALSE;
522         }
523
524         set_properties (G_OBJECT (src), props_table);
525         ges_layer_add_clip (layer, GES_CLIP (src));
526
527         g_signal_connect (src, "child-added",
528             G_CALLBACK (track_element_added_cb), props_table);
529
530         priv->sources_to_load = g_list_prepend (priv->sources_to_load, src);
531       }
532
533     } else {
534       GESEffect *effect;
535       gchar *active = (gchar *) g_hash_table_lookup (props_table, "active");
536
537       effect = ges_effect_new ((gchar *)
538           g_hash_table_lookup (props_table, (gchar *) "effect_name"));
539       ges_track_element_set_track_type (GES_TRACK_ELEMENT (effect),
540           (video ? GES_TRACK_TYPE_VIDEO : GES_TRACK_TYPE_AUDIO));
541       effect_table =
542           g_hash_table_lookup (props_table, (gchar *) "effect_props");
543
544       if (!ges_container_add (GES_CONTAINER (src),
545               GES_TIMELINE_ELEMENT (effect))) {
546         GST_ERROR ("%p could not add %p while"
547             " reloading, this should never happen", src, effect);
548       }
549
550       if (!g_strcmp0 (active, (gchar *) "(bool)False"))
551         ges_track_element_set_active (GES_TRACK_ELEMENT (effect), FALSE);
552
553       /* Set effect properties */
554       keys = g_hash_table_get_keys (effect_table);
555       for (tmp_key = keys; tmp_key; tmp_key = tmp_key->next) {
556         GstStructure *structure;
557         const GValue *value;
558         GParamSpec *spec;
559         GstCaps *caps;
560         gchar *prop_val;
561
562         prop_val = (gchar *) g_hash_table_lookup (effect_table,
563             (gchar *) tmp_key->data);
564
565         if (g_strstr_len (prop_val, -1, "(GEnum)")) {
566           gchar **val = g_strsplit (prop_val, ")", 2);
567
568           ges_track_element_set_child_properties (GES_TRACK_ELEMENT (effect),
569               (gchar *) tmp_key->data, atoi (val[1]), NULL);
570           g_strfreev (val);
571
572         } else if (ges_track_element_lookup_child (GES_TRACK_ELEMENT (effect),
573                 (gchar *) tmp->data, NULL, &spec)) {
574           gchar *caps_str = g_strdup_printf ("structure1, property1=%s;",
575               prop_val);
576
577           caps = gst_caps_from_string (caps_str);
578           g_free (caps_str);
579           structure = gst_caps_get_structure (caps, 0);
580           value = gst_structure_get_value (structure, "property1");
581
582           ges_track_element_set_child_property_by_pspec (GES_TRACK_ELEMENT
583               (effect), spec, (GValue *) value);
584           gst_caps_unref (caps);
585         }
586       }
587     }
588   }
589
590   if (a_avail) {
591     ges_clip_set_supported_formats (GES_CLIP (src), GES_TRACK_TYPE_VIDEO);
592   } else if (v_avail) {
593     ges_clip_set_supported_formats (GES_CLIP (src), GES_TRACK_TYPE_AUDIO);
594   }
595 }
596
597 static gboolean
598 make_clips (GESFormatter * self)
599 {
600   GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
601   GHashTable *source_table;
602
603   GList *keys = NULL, *tmp = NULL, *reflist = NULL;
604
605   keys = g_hash_table_get_keys (priv->clips_table);
606
607   for (tmp = keys; tmp; tmp = tmp->next) {
608     gchar *fac_id = (gchar *) tmp->data;
609
610     reflist = g_hash_table_lookup (priv->clips_table, fac_id);
611     source_table = g_hash_table_lookup (priv->sources_table, fac_id);
612     make_source (self, reflist, source_table);
613   }
614
615   g_list_free (keys);
616   return TRUE;
617 }
618
619 static gboolean
620 load_pitivi_file_from_uri (GESFormatter * self,
621     GESTimeline * timeline, const gchar * uri, GError ** error)
622 {
623   xmlDocPtr doc;
624   GESLayer *layer;
625   GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
626
627   gboolean ret = TRUE;
628   gint *prio = malloc (sizeof (gint));
629
630   *prio = 0;
631   layer = ges_layer_new ();
632   g_object_set (layer, "auto-transition", TRUE, NULL);
633
634   g_hash_table_insert (priv->layers_table, prio, layer);
635   g_object_set (layer, "priority", (gint32) 0, NULL);
636
637   if (!ges_timeline_add_layer (timeline, layer)) {
638     GST_ERROR ("Couldn't add layer");
639     return FALSE;
640   }
641
642   if (!(doc = xmlParseFile (uri))) {
643     GST_ERROR ("The xptv file for uri %s was badly formed or did not exist",
644         uri);
645     return FALSE;
646   }
647
648   priv->xpathCtx = xmlXPathNewContext (doc);
649
650   if (self->project)
651     parse_metadatas (self);
652
653   if (!create_tracks (self)) {
654     GST_ERROR ("Couldn't create tracks");
655     return FALSE;
656   }
657
658   list_sources (self);
659
660   if (!parse_clips (self)) {
661     GST_ERROR ("Couldn't find clips markup in the xptv file");
662     return FALSE;
663   }
664
665   if (!parse_track_elements (self)) {
666     GST_ERROR ("Couldn't find track objects markup in the xptv file");
667     return FALSE;
668   }
669
670
671
672   /* If there are no clips to load we should emit
673    * 'project-loaded' signal.
674    */
675   if (!g_hash_table_size (priv->clips_table) && GES_FORMATTER (self)->project) {
676     ges_project_set_loaded (GES_FORMATTER (self)->project,
677         GES_FORMATTER (self), NULL);
678   } else {
679     if (!make_clips (self)) {
680       GST_ERROR ("Couldn't deserialise the project properly");
681       return FALSE;
682     }
683   }
684
685   xmlXPathFreeContext (priv->xpathCtx);
686   xmlFreeDoc (doc);
687   return ret;
688 }
689
690 /* Object functions */
691 static void
692 ges_pitivi_formatter_finalize (GObject * object)
693 {
694   GESPitiviFormatter *self = GES_PITIVI_FORMATTER (object);
695   GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
696
697   g_hash_table_destroy (priv->sources_table);
698   g_hash_table_destroy (priv->source_uris);
699
700   g_hash_table_destroy (priv->saving_source_table);
701   g_list_free (priv->sources_to_load);
702
703   if (priv->clips_table != NULL) {
704     g_hash_table_foreach (priv->clips_table,
705         (GHFunc) list_table_destroyer, NULL);
706     g_hash_table_destroy (priv->clips_table);
707   }
708
709   if (priv->layers_table != NULL)
710     g_hash_table_destroy (priv->layers_table);
711
712   if (priv->track_elements_table != NULL) {
713     g_hash_table_destroy (priv->track_elements_table);
714   }
715
716   G_OBJECT_CLASS (ges_pitivi_formatter_parent_class)->finalize (object);
717 }
718
719 static void
720 ges_pitivi_formatter_class_init (GESPitiviFormatterClass * klass)
721 {
722   GESFormatterClass *formatter_klass;
723   GObjectClass *object_class;
724
725   GST_DEBUG_CATEGORY_INIT (ges_pitivi_formatter_debug, "ges_pitivi_formatter",
726       GST_DEBUG_FG_YELLOW, "ges pitivi formatter");
727
728   object_class = G_OBJECT_CLASS (klass);
729   formatter_klass = GES_FORMATTER_CLASS (klass);
730
731   formatter_klass->can_load_uri = pitivi_can_load_uri;
732   formatter_klass->save_to_uri = NULL;
733   formatter_klass->load_from_uri = load_pitivi_file_from_uri;
734   object_class->finalize = ges_pitivi_formatter_finalize;
735
736   ges_formatter_class_register_metas (formatter_klass, "pitivi",
737       "Legacy Pitivi project files", "xptv", "text/x-xptv",
738       DOUBLE_VERSION, GST_RANK_MARGINAL);
739 }
740
741 static void
742 ges_pitivi_formatter_init (GESPitiviFormatter * self)
743 {
744   GESPitiviFormatterPrivate *priv;
745
746   self->priv = ges_pitivi_formatter_get_instance_private (self);
747
748   priv = self->priv;
749
750   priv->track_elements_table =
751       g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
752       (GDestroyNotify) g_hash_table_destroy);
753
754   priv->clips_table =
755       g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
756
757   priv->layers_table =
758       g_hash_table_new_full (g_int_hash, g_str_equal, g_free, gst_object_unref);
759
760   priv->sources_table =
761       g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
762       (GDestroyNotify) g_hash_table_destroy);
763
764   priv->source_uris =
765       g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
766
767   priv->sources_to_load = NULL;
768
769   /* Saving context */
770   priv->saving_source_table =
771       g_hash_table_new_full (g_str_hash, g_int_equal, g_free, g_free);
772   priv->nb_sources = 1;
773 }
774
775 GESPitiviFormatter *
776 ges_pitivi_formatter_new (void)
777 {
778   return g_object_new (GES_TYPE_PITIVI_FORMATTER, NULL);
779 }