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
list_table_destroyer(gpointer key,gpointer value,void * unused)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
pitivi_can_load_uri(GESFormatter * dummy_instance,const gchar * uri,GError ** error)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 *
get_nodes_infos(xmlNodePtr node)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
create_tracks(GESFormatter * self)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
parse_metadatas(GESFormatter * self)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
234 static void
list_sources(GESFormatter * self)235 list_sources (GESFormatter * self)
236 {
237 GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
238 xmlXPathObjectPtr xpathObj;
239 GHashTable *table;
240 int size, j;
241 gchar *id, *filename;
242 xmlNodeSetPtr nodes;
243
244 xpathObj = xmlXPathEvalExpression ((const xmlChar *)
245 "/pitivi/factories/sources/source", priv->xpathCtx);
246 nodes = xpathObj->nodesetval;
247
248 size = (nodes) ? nodes->nodeNr : 0;
249 for (j = 0; j < size; ++j) {
250 table = get_nodes_infos (nodes->nodeTab[j]);
251 id = (gchar *) g_hash_table_lookup (table, (gchar *) "id");
252 filename = (gchar *) g_hash_table_lookup (table, (gchar *) "filename");
253 g_hash_table_insert (priv->sources_table, g_strdup (id), table);
254 g_hash_table_insert (priv->source_uris, g_strdup (filename),
255 g_strdup (filename));
256 if (self->project)
257 ges_project_create_asset (self->project, filename, GES_TYPE_URI_CLIP);
258 }
259
260 xmlXPathFreeObject (xpathObj);
261 }
262
263 static gboolean
parse_track_elements(GESFormatter * self)264 parse_track_elements (GESFormatter * self)
265 {
266 GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
267 xmlXPathObjectPtr xpathObj;
268 xmlNodeSetPtr nodes;
269 int size, j;
270 gchar *id, *fac_ref;
271 GHashTable *table = NULL, *effect_table = NULL;
272 xmlNode *first_child;
273 gchar *media_type;
274
275 /* FIXME Make this whole function cleaner starting from
276 * "/pitivi/timeline/tracks/track/stream" and descending
277 * into the children. */
278 xpathObj = xmlXPathEvalExpression ((const xmlChar *)
279 "/pitivi/timeline/tracks/track/track-objects/track-object",
280 priv->xpathCtx);
281
282 if (xpathObj == NULL) {
283 GST_DEBUG ("No track object found");
284
285 return FALSE;
286 }
287
288 nodes = xpathObj->nodesetval;
289 size = (nodes) ? nodes->nodeNr : 0;
290
291 for (j = 0; j < size; ++j) {
292 xmlNodePtr node = nodes->nodeTab[j];
293
294 table = get_nodes_infos (nodes->nodeTab[j]);
295 id = (gchar *) g_hash_table_lookup (table, (gchar *) "id");
296 first_child = nodes->nodeTab[j]->children->next;
297 fac_ref = (gchar *) xmlGetProp (first_child, (xmlChar *) "id");
298
299 /* We check if the first child is "effect" */
300 if (!g_strcmp0 ((gchar *) first_child->name, (gchar *) "effect")) {
301 xmlChar *effect_name;
302 xmlNodePtr fact_node = first_child->children->next;
303
304 /* We have a node called "text" in between thus ->next->next */
305 xmlNodePtr elem_props_node = fact_node->next->next;
306
307 effect_name = xmlGetProp (fact_node, (xmlChar *) "name");
308 g_hash_table_insert (table, g_strdup ((gchar *) "effect_name"),
309 g_strdup ((gchar *) effect_name));
310 xmlFree (effect_name);
311
312 /* We put the effects properties in an hacktable (Lapsus is on :) */
313 effect_table = get_nodes_infos (elem_props_node);
314
315 g_hash_table_insert (table, g_strdup ((gchar *) "fac_ref"),
316 g_strdup ("effect"));
317
318 xmlFree (fac_ref);
319 } else {
320
321 g_hash_table_insert (table, g_strdup ((gchar *) "fac_ref"),
322 g_strdup (fac_ref));
323 xmlFree (fac_ref);
324 }
325
326 /* Same as before, we got a text node in between, thus the 2 prev
327 * node->parent is <track-objects>, the one before is <stream>
328 */
329 media_type = (gchar *) xmlGetProp (node->parent->prev->prev,
330 (const xmlChar *) "type");
331 g_hash_table_insert (table, g_strdup ((gchar *) "media_type"),
332 g_strdup (media_type));
333 xmlFree (media_type);
334
335
336 if (effect_table)
337 g_hash_table_insert (table, g_strdup ("effect_props"), effect_table);
338
339 g_hash_table_insert (priv->track_elements_table, g_strdup (id), table);
340 }
341
342 xmlXPathFreeObject (xpathObj);
343 return TRUE;
344 }
345
346 static gboolean
parse_clips(GESFormatter * self)347 parse_clips (GESFormatter * self)
348 {
349 int size, j;
350 xmlNodeSetPtr nodes;
351 xmlXPathObjectPtr xpathObj;
352 xmlNodePtr clip_nd, tmp_nd, tmp_nd2;
353 xmlChar *trackelementrefId, *facrefId = NULL;
354
355 GList *reflist = NULL;
356 GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
357 GHashTable *clips_table = priv->clips_table;
358
359 xpathObj = xmlXPathEvalExpression ((const xmlChar *)
360 "/pitivi/timeline/timeline-objects/timeline-object", priv->xpathCtx);
361
362 if (xpathObj == NULL) {
363 xmlXPathFreeObject (xpathObj);
364 return FALSE;
365 }
366
367 nodes = xpathObj->nodesetval;
368 size = (nodes) ? nodes->nodeNr : 0;
369
370 for (j = 0; j < size; j++) {
371 clip_nd = nodes->nodeTab[j];
372
373 for (tmp_nd = clip_nd->children; tmp_nd; tmp_nd = tmp_nd->next) {
374 /* We assume that factory-ref is always before the tckobjs-ref */
375 if (!xmlStrcmp (tmp_nd->name, (xmlChar *) "factory-ref")) {
376 facrefId = xmlGetProp (tmp_nd, (xmlChar *) "id");
377
378 } else if (!xmlStrcmp (tmp_nd->name, (xmlChar *) "track-object-refs")) {
379
380 for (tmp_nd2 = tmp_nd->children; tmp_nd2; tmp_nd2 = tmp_nd2->next) {
381 if (!xmlStrcmp (tmp_nd2->name, (xmlChar *) "track-object-ref")) {
382 /* We add the track object ref ID to the list of the current
383 * Clip tracks, this way we can merge 2
384 * Clip-s into 1 when we have unlinked TrackElement-s */
385 reflist = g_hash_table_lookup (clips_table, facrefId);
386 trackelementrefId = xmlGetProp (tmp_nd2, (xmlChar *) "id");
387 reflist =
388 g_list_append (reflist, g_strdup ((gchar *) trackelementrefId));
389 g_hash_table_insert (clips_table, g_strdup ((gchar *) facrefId),
390 reflist);
391
392 xmlFree (trackelementrefId);
393 }
394 }
395 }
396 }
397 }
398
399 xmlXPathFreeObject (xpathObj);
400 return TRUE;
401 }
402
403 static void
set_properties(GObject * obj,GHashTable * props_table)404 set_properties (GObject * obj, GHashTable * props_table)
405 {
406 gint i;
407 gchar **prop_array, *valuestr;
408 gint64 value;
409
410 gchar props[3][10] = { "duration", "in_point", "start" };
411
412 for (i = 0; i < 3; i++) {
413 valuestr = g_hash_table_lookup (props_table, props[i]);
414 prop_array = g_strsplit (valuestr, ")", 0);
415 value = g_ascii_strtoll (prop_array[1], NULL, 0);
416 g_object_set (obj, props[i], value, NULL);
417
418 g_strfreev (prop_array);
419 }
420 }
421
422 static void
track_element_added_cb(GESClip * clip,GESTrackElement * track_element,GHashTable * props_table)423 track_element_added_cb (GESClip * clip,
424 GESTrackElement * track_element, GHashTable * props_table)
425 {
426 GESPitiviFormatter *formatter;
427
428 formatter = GES_PITIVI_FORMATTER (g_hash_table_lookup (props_table,
429 "current-formatter"));
430 if (formatter) {
431 GESPitiviFormatterPrivate *priv = formatter->priv;
432
433 /* Make sure the hack to get a ref to the formatter
434 * doesn't break everything */
435 g_hash_table_steal (props_table, "current-formatter");
436
437 priv->sources_to_load = g_list_remove (priv->sources_to_load, clip);
438 if (!priv->sources_to_load && GES_FORMATTER (formatter)->project)
439 ges_project_set_loaded (GES_FORMATTER (formatter)->project,
440 GES_FORMATTER (formatter));
441 }
442
443 /* Disconnect the signal */
444 g_signal_handlers_disconnect_by_func (clip, track_element_added_cb,
445 props_table);
446 }
447
448 static void
make_source(GESFormatter * self,GList * reflist,GHashTable * source_table)449 make_source (GESFormatter * self, GList * reflist, GHashTable * source_table)
450 {
451 GHashTable *props_table, *effect_table;
452 gchar **prio_array;
453 GESLayer *layer;
454 GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
455
456 gchar *fac_ref = NULL, *media_type = NULL, *filename = NULL, *prio_str;
457 GList *tmp = NULL, *keys, *tmp_key;
458 GESUriClip *src = NULL;
459 gint prio;
460 gboolean a_avail = FALSE, v_avail = FALSE, video;
461 GHashTable *trackelement_table = priv->track_elements_table;
462
463 for (tmp = reflist; tmp; tmp = tmp->next) {
464
465 /* Get the layer */
466 props_table = g_hash_table_lookup (trackelement_table, (gchar *) tmp->data);
467 prio_str = (gchar *) g_hash_table_lookup (props_table, "priority");
468 prio_array = g_strsplit (prio_str, ")", 0);
469 prio = (gint) g_ascii_strtod (prio_array[1], NULL);
470 g_strfreev (prio_array);
471
472 /* If we do not have any layer with this priority, create it */
473 if (!(layer = g_hash_table_lookup (priv->layers_table, &prio))) {
474 layer = ges_layer_new ();
475 g_object_set (layer, "auto-transition", TRUE, "priority", prio, NULL);
476 ges_timeline_add_layer (self->timeline, layer);
477 g_hash_table_insert (priv->layers_table, g_memdup (&prio,
478 sizeof (guint64)), layer);
479 }
480
481 fac_ref = (gchar *) g_hash_table_lookup (props_table, "fac_ref");
482 media_type = (gchar *) g_hash_table_lookup (props_table, "media_type");
483
484 if (!g_strcmp0 (media_type, "pitivi.stream.VideoStream"))
485 video = TRUE;
486 else
487 video = FALSE;
488
489 /* FIXME I am sure we could reimplement this whole part
490 * in a simpler way */
491
492 if (g_strcmp0 (fac_ref, (gchar *) "effect")) {
493 /* FIXME this is a hack to get a ref to the formatter when receiving
494 * child-added */
495 g_hash_table_insert (props_table, (gchar *) "current-formatter", self);
496 if (a_avail && (!video)) {
497 a_avail = FALSE;
498 } else if (v_avail && (video)) {
499 v_avail = FALSE;
500 } else {
501
502 /* If we only have audio or only video in the previous source,
503 * set it has such */
504 if (a_avail) {
505 ges_clip_set_supported_formats (GES_CLIP (src), GES_TRACK_TYPE_VIDEO);
506 } else if (v_avail) {
507 ges_clip_set_supported_formats (GES_CLIP (src), GES_TRACK_TYPE_AUDIO);
508 }
509
510 filename = (gchar *) g_hash_table_lookup (source_table, "filename");
511
512 src = ges_uri_clip_new (filename);
513
514 if (!video) {
515 v_avail = TRUE;
516 a_avail = FALSE;
517 } else {
518 a_avail = TRUE;
519 v_avail = FALSE;
520 }
521
522 set_properties (G_OBJECT (src), props_table);
523 ges_layer_add_clip (layer, GES_CLIP (src));
524
525 g_signal_connect (src, "child-added",
526 G_CALLBACK (track_element_added_cb), props_table);
527
528 priv->sources_to_load = g_list_prepend (priv->sources_to_load, src);
529 }
530
531 } else {
532 GESEffect *effect;
533 gchar *active = (gchar *) g_hash_table_lookup (props_table, "active");
534
535 effect = ges_effect_new ((gchar *)
536 g_hash_table_lookup (props_table, (gchar *) "effect_name"));
537 ges_track_element_set_track_type (GES_TRACK_ELEMENT (effect),
538 (video ? GES_TRACK_TYPE_VIDEO : GES_TRACK_TYPE_AUDIO));
539 effect_table =
540 g_hash_table_lookup (props_table, (gchar *) "effect_props");
541
542 ges_container_add (GES_CONTAINER (src), GES_TIMELINE_ELEMENT (effect));
543
544 if (!g_strcmp0 (active, (gchar *) "(bool)False"))
545 ges_track_element_set_active (GES_TRACK_ELEMENT (effect), FALSE);
546
547 /* Set effect properties */
548 keys = g_hash_table_get_keys (effect_table);
549 for (tmp_key = keys; tmp_key; tmp_key = tmp_key->next) {
550 GstStructure *structure;
551 const GValue *value;
552 GParamSpec *spec;
553 GstCaps *caps;
554 gchar *prop_val;
555
556 prop_val = (gchar *) g_hash_table_lookup (effect_table,
557 (gchar *) tmp_key->data);
558
559 if (g_strstr_len (prop_val, -1, "(GEnum)")) {
560 gchar **val = g_strsplit (prop_val, ")", 2);
561
562 ges_track_element_set_child_properties (GES_TRACK_ELEMENT (effect),
563 (gchar *) tmp_key->data, atoi (val[1]), NULL);
564 g_strfreev (val);
565
566 } else if (ges_track_element_lookup_child (GES_TRACK_ELEMENT (effect),
567 (gchar *) tmp->data, NULL, &spec)) {
568 gchar *caps_str = g_strdup_printf ("structure1, property1=%s;",
569 prop_val);
570
571 caps = gst_caps_from_string (caps_str);
572 g_free (caps_str);
573 structure = gst_caps_get_structure (caps, 0);
574 value = gst_structure_get_value (structure, "property1");
575
576 ges_track_element_set_child_property_by_pspec (GES_TRACK_ELEMENT
577 (effect), spec, (GValue *) value);
578 gst_caps_unref (caps);
579 }
580 }
581 }
582 }
583
584 if (a_avail) {
585 ges_clip_set_supported_formats (GES_CLIP (src), GES_TRACK_TYPE_VIDEO);
586 } else if (v_avail) {
587 ges_clip_set_supported_formats (GES_CLIP (src), GES_TRACK_TYPE_AUDIO);
588 }
589 }
590
591 static gboolean
make_clips(GESFormatter * self)592 make_clips (GESFormatter * self)
593 {
594 GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
595 GHashTable *source_table;
596
597 GList *keys = NULL, *tmp = NULL, *reflist = NULL;
598
599 keys = g_hash_table_get_keys (priv->clips_table);
600
601 for (tmp = keys; tmp; tmp = tmp->next) {
602 gchar *fac_id = (gchar *) tmp->data;
603
604 reflist = g_hash_table_lookup (priv->clips_table, fac_id);
605 source_table = g_hash_table_lookup (priv->sources_table, fac_id);
606 make_source (self, reflist, source_table);
607 }
608
609 g_list_free (keys);
610 return TRUE;
611 }
612
613 static gboolean
load_pitivi_file_from_uri(GESFormatter * self,GESTimeline * timeline,const gchar * uri,GError ** error)614 load_pitivi_file_from_uri (GESFormatter * self,
615 GESTimeline * timeline, const gchar * uri, GError ** error)
616 {
617 xmlDocPtr doc;
618 GESLayer *layer;
619 GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
620
621 gboolean ret = TRUE;
622 gint *prio = malloc (sizeof (gint));
623
624 *prio = 0;
625 layer = ges_layer_new ();
626 g_object_set (layer, "auto-transition", TRUE, NULL);
627
628 g_hash_table_insert (priv->layers_table, prio, layer);
629 g_object_set (layer, "priority", (gint32) 0, NULL);
630
631 if (!ges_timeline_add_layer (timeline, layer)) {
632 GST_ERROR ("Couldn't add layer");
633 return FALSE;
634 }
635
636 if (!(doc = xmlParseFile (uri))) {
637 GST_ERROR ("The xptv file for uri %s was badly formed or did not exist",
638 uri);
639 return FALSE;
640 }
641
642 priv->xpathCtx = xmlXPathNewContext (doc);
643
644 if (self->project)
645 parse_metadatas (self);
646
647 if (!create_tracks (self)) {
648 GST_ERROR ("Couldn't create tracks");
649 return FALSE;
650 }
651
652 list_sources (self);
653
654 if (!parse_clips (self)) {
655 GST_ERROR ("Couldn't find clips markup in the xptv file");
656 return FALSE;
657 }
658
659 if (!parse_track_elements (self)) {
660 GST_ERROR ("Couldn't find track objects markup in the xptv file");
661 return FALSE;
662 }
663
664
665
666 /* If there are no clips to load we should emit
667 * 'project-loaded' signal.
668 */
669 if (!g_hash_table_size (priv->clips_table) && GES_FORMATTER (self)->project) {
670 ges_project_set_loaded (GES_FORMATTER (self)->project,
671 GES_FORMATTER (self));
672 } else {
673 if (!make_clips (self)) {
674 GST_ERROR ("Couldn't deserialise the project properly");
675 return FALSE;
676 }
677 }
678
679 xmlXPathFreeContext (priv->xpathCtx);
680 xmlFreeDoc (doc);
681 return ret;
682 }
683
684 /* Object functions */
685 static void
ges_pitivi_formatter_finalize(GObject * object)686 ges_pitivi_formatter_finalize (GObject * object)
687 {
688 GESPitiviFormatter *self = GES_PITIVI_FORMATTER (object);
689 GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
690
691 g_hash_table_destroy (priv->sources_table);
692 g_hash_table_destroy (priv->source_uris);
693
694 g_hash_table_destroy (priv->saving_source_table);
695 g_list_free (priv->sources_to_load);
696
697 if (priv->clips_table != NULL) {
698 g_hash_table_foreach (priv->clips_table,
699 (GHFunc) list_table_destroyer, NULL);
700 g_hash_table_destroy (priv->clips_table);
701 }
702
703 if (priv->layers_table != NULL)
704 g_hash_table_destroy (priv->layers_table);
705
706 if (priv->track_elements_table != NULL) {
707 g_hash_table_destroy (priv->track_elements_table);
708 }
709
710 G_OBJECT_CLASS (ges_pitivi_formatter_parent_class)->finalize (object);
711 }
712
713 static void
ges_pitivi_formatter_class_init(GESPitiviFormatterClass * klass)714 ges_pitivi_formatter_class_init (GESPitiviFormatterClass * klass)
715 {
716 GESFormatterClass *formatter_klass;
717 GObjectClass *object_class;
718
719 GST_DEBUG_CATEGORY_INIT (ges_pitivi_formatter_debug, "ges_pitivi_formatter",
720 GST_DEBUG_FG_YELLOW, "ges pitivi formatter");
721
722 object_class = G_OBJECT_CLASS (klass);
723 formatter_klass = GES_FORMATTER_CLASS (klass);
724
725 formatter_klass->can_load_uri = pitivi_can_load_uri;
726 formatter_klass->save_to_uri = NULL;
727 formatter_klass->load_from_uri = load_pitivi_file_from_uri;
728 object_class->finalize = ges_pitivi_formatter_finalize;
729
730 ges_formatter_class_register_metas (formatter_klass, "pitivi",
731 "Legacy Pitivi project files", "xptv", "text/x-xptv",
732 DOUBLE_VERSION, GST_RANK_MARGINAL);
733 }
734
735 static void
ges_pitivi_formatter_init(GESPitiviFormatter * self)736 ges_pitivi_formatter_init (GESPitiviFormatter * self)
737 {
738 GESPitiviFormatterPrivate *priv;
739
740 self->priv = ges_pitivi_formatter_get_instance_private (self);
741
742 priv = self->priv;
743
744 priv->track_elements_table =
745 g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
746 (GDestroyNotify) g_hash_table_destroy);
747
748 priv->clips_table =
749 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
750
751 priv->layers_table =
752 g_hash_table_new_full (g_int_hash, g_str_equal, g_free, gst_object_unref);
753
754 priv->sources_table =
755 g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
756 (GDestroyNotify) g_hash_table_destroy);
757
758 priv->source_uris =
759 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
760
761 priv->sources_to_load = NULL;
762
763 /* Saving context */
764 priv->saving_source_table =
765 g_hash_table_new_full (g_str_hash, g_int_equal, g_free, g_free);
766 priv->nb_sources = 1;
767 }
768
769 GESPitiviFormatter *
ges_pitivi_formatter_new(void)770 ges_pitivi_formatter_new (void)
771 {
772 return g_object_new (GES_TYPE_PITIVI_FORMATTER, NULL);
773 }
774