6d99421b1f0c4c746ab087f555d38045d340837c
[platform/upstream/gst-editing-services.git] / ges / ges-xml-formatter.c
1 /* Gstreamer Editing Services
2  *
3  * Copyright (C) <2012> Thibault Saunier <thibault.saunier@collabora.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #undef VERSION
23 #endif
24
25
26 /* TODO Determine error codes numbers */
27
28 #include <string.h>
29 #include <errno.h>
30 #include <locale.h>
31
32 #include "ges.h"
33 #include "ges-internal.h"
34
35 #define parent_class ges_xml_formatter_parent_class
36 #define API_VERSION 0
37 #define MINOR_VERSION 5
38 #define VERSION 0.5
39
40 #define COLLECT_STR_OPT (G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL)
41
42 #define _GET_PRIV(o) (((GESXmlFormatter*)o)->priv)
43
44 struct _GESXmlFormatterPrivate
45 {
46   gboolean ges_opened;
47   gboolean project_opened;
48
49   GString *str;
50
51   GHashTable *element_id;
52
53   guint nbelements;
54
55   guint min_version;
56 };
57
58 G_DEFINE_TYPE_WITH_PRIVATE (GESXmlFormatter, ges_xml_formatter,
59     GES_TYPE_BASE_XML_FORMATTER);
60
61 static inline void
62 _parse_ges_element (GMarkupParseContext * context, const gchar * element_name,
63     const gchar ** attribute_names, const gchar ** attribute_values,
64     GESXmlFormatter * self, GError ** error)
65 {
66   guint api_version;
67   const gchar *version, *properties;
68
69   gchar **split_version = NULL;
70
71   if (g_strcmp0 (element_name, "ges")) {
72     g_set_error (error, G_MARKUP_ERROR,
73         G_MARKUP_ERROR_INVALID_CONTENT,
74         "element '%s', Missing <ges> element'", element_name);
75     return;
76   }
77
78   if (!g_markup_collect_attributes (element_name, attribute_names,
79           attribute_values, error, G_MARKUP_COLLECT_STRING, "version", &version,
80           COLLECT_STR_OPT, "properties", &properties,
81           G_MARKUP_COLLECT_INVALID)) {
82     return;
83   }
84
85   split_version = g_strsplit (version, ".", 2);
86   if (split_version[1] == NULL)
87     goto failed;
88
89   errno = 0;
90   api_version = g_ascii_strtoull (split_version[0], NULL, 10);
91   if (errno || api_version != API_VERSION)
92     goto stroull_failed;
93
94   self->priv->min_version = g_ascii_strtoull (split_version[1], NULL, 10);
95   if (self->priv->min_version > MINOR_VERSION)
96     goto failed;
97
98   _GET_PRIV (self)->ges_opened = TRUE;
99   g_strfreev (split_version);
100   return;
101
102 failed:
103   g_set_error (error, G_MARKUP_ERROR,
104       G_MARKUP_ERROR_INVALID_CONTENT,
105       "element '%s', %s wrong version'", element_name, version);
106   if (split_version)
107     g_strfreev (split_version);
108
109   return;
110
111 stroull_failed:
112   GST_WARNING_OBJECT (self, "Error while strtoull: %s", g_strerror (errno));
113   goto failed;
114 }
115
116 static inline void
117 _parse_project (GMarkupParseContext * context, const gchar * element_name,
118     const gchar ** attribute_names, const gchar ** attribute_values,
119     GESXmlFormatter * self, GError ** error)
120 {
121   const gchar *metadatas = NULL, *properties;
122   GESXmlFormatterPrivate *priv = _GET_PRIV (self);
123
124   if (g_strcmp0 (element_name, "project")) {
125     g_set_error (error, G_MARKUP_ERROR,
126         G_MARKUP_ERROR_INVALID_CONTENT,
127         "Found element '%s', Missing '<project>' element'", element_name);
128   } else {
129     priv->project_opened = TRUE;
130     if (!g_markup_collect_attributes (element_name, attribute_names,
131             attribute_values, error,
132             COLLECT_STR_OPT, "properties", &properties,
133             COLLECT_STR_OPT, "metadatas", &metadatas, G_MARKUP_COLLECT_INVALID))
134       return;
135
136     if (GES_FORMATTER (self)->project && metadatas)
137       ges_meta_container_add_metas_from_string (GES_META_CONTAINER
138           (GES_FORMATTER (self)->project), metadatas);
139
140   }
141 }
142
143 static inline void
144 _parse_encoding_profile (GMarkupParseContext * context,
145     const gchar * element_name, const gchar ** attribute_names,
146     const gchar ** attribute_values, GESXmlFormatter * self, GError ** error)
147 {
148   GstCaps *capsformat = NULL;
149   GstStructure *preset_properties = NULL;
150   const gchar *name, *description, *type, *preset = NULL,
151       *str_preset_properties = NULL, *preset_name = NULL, *format;
152
153   if (!g_markup_collect_attributes (element_name, attribute_names,
154           attribute_values, error,
155           G_MARKUP_COLLECT_STRING, "name", &name,
156           G_MARKUP_COLLECT_STRING, "description", &description,
157           G_MARKUP_COLLECT_STRING, "type", &type,
158           COLLECT_STR_OPT, "preset", &preset,
159           COLLECT_STR_OPT, "preset-properties", &str_preset_properties,
160           COLLECT_STR_OPT, "preset-name", &preset_name,
161           COLLECT_STR_OPT, "format", &format, G_MARKUP_COLLECT_INVALID))
162     return;
163
164   if (format)
165     capsformat = gst_caps_from_string (format);
166
167   if (str_preset_properties) {
168     preset_properties = gst_structure_from_string (str_preset_properties, NULL);
169     if (preset_properties == NULL) {
170       g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
171           "element '%s', Wrong preset-properties format.", element_name);
172       return;
173     }
174   }
175
176   ges_base_xml_formatter_add_encoding_profile (GES_BASE_XML_FORMATTER (self),
177       type, NULL, name, description, capsformat, preset, preset_properties,
178       preset_name, 0, 0, NULL, 0, FALSE, NULL, TRUE, error);
179 }
180
181 static inline void
182 _parse_stream_profile (GMarkupParseContext * context,
183     const gchar * element_name, const gchar ** attribute_names,
184     const gchar ** attribute_values, GESXmlFormatter * self, GError ** error)
185 {
186   gboolean variableframerate = FALSE, enabled = TRUE;
187   guint id = 0, presence = 0, pass = 0;
188   GstCaps *format_caps = NULL, *restriction_caps = NULL;
189   GstStructure *preset_properties = NULL;
190   const gchar *parent, *strid, *type, *strpresence, *format = NULL,
191       *name = NULL, *description = NULL, *preset,
192       *str_preset_properties = NULL, *preset_name = NULL, *restriction = NULL,
193       *strpass = NULL, *strvariableframerate = NULL, *strenabled = NULL;
194
195   /* FIXME Looks like there is a bug in that function, if we put the parent
196    * at the beginning it set %NULL and not the real value... :/ */
197   if (!g_markup_collect_attributes (element_name, attribute_names,
198           attribute_values, error,
199           G_MARKUP_COLLECT_STRING, "id", &strid,
200           G_MARKUP_COLLECT_STRING, "type", &type,
201           G_MARKUP_COLLECT_STRING, "presence", &strpresence,
202           COLLECT_STR_OPT, "format", &format,
203           COLLECT_STR_OPT, "name", &name,
204           COLLECT_STR_OPT, "description", &description,
205           COLLECT_STR_OPT, "preset", &preset,
206           COLLECT_STR_OPT, "preset-properties", &str_preset_properties,
207           COLLECT_STR_OPT, "preset-name", &preset_name,
208           COLLECT_STR_OPT, "restriction", &restriction,
209           COLLECT_STR_OPT, "pass", &strpass,
210           COLLECT_STR_OPT, "variableframerate", &strvariableframerate,
211           COLLECT_STR_OPT, "enabled", &strenabled,
212           G_MARKUP_COLLECT_STRING, "parent", &parent, G_MARKUP_COLLECT_INVALID))
213     return;
214
215   errno = 0;
216   id = g_ascii_strtoll (strid, NULL, 10);
217   if (errno)
218     goto convertion_failed;
219
220   if (strpresence) {
221     presence = g_ascii_strtoll (strpresence, NULL, 10);
222     if (errno)
223       goto convertion_failed;
224   }
225
226   if (str_preset_properties) {
227     preset_properties = gst_structure_from_string (str_preset_properties, NULL);
228     if (preset_properties == NULL)
229       goto convertion_failed;
230   }
231
232   if (strpass) {
233     pass = g_ascii_strtoll (strpass, NULL, 10);
234     if (errno)
235       goto convertion_failed;
236   }
237
238   if (strvariableframerate) {
239     variableframerate = g_ascii_strtoll (strvariableframerate, NULL, 10);
240     if (errno)
241       goto convertion_failed;
242   }
243
244   if (strenabled) {
245     enabled = g_ascii_strtoll (strenabled, NULL, 10);
246     if (errno)
247       goto convertion_failed;
248   }
249
250   if (format)
251     format_caps = gst_caps_from_string (format);
252
253   if (restriction)
254     restriction_caps = gst_caps_from_string (restriction);
255
256   ges_base_xml_formatter_add_encoding_profile (GES_BASE_XML_FORMATTER (self),
257       type, parent, name, description, format_caps, preset, preset_properties,
258       preset_name, id, presence, restriction_caps, pass, variableframerate,
259       NULL, enabled, error);
260
261   if (preset_properties)
262     gst_structure_free (preset_properties);
263
264   return;
265
266 convertion_failed:
267   g_set_error (error, G_MARKUP_ERROR,
268       G_MARKUP_ERROR_INVALID_CONTENT,
269       "element '%s', Wrong property type, error: %s'", element_name,
270       g_strerror (errno));
271   return;
272 }
273
274 static inline void
275 _parse_timeline (GMarkupParseContext * context, const gchar * element_name,
276     const gchar ** attribute_names, const gchar ** attribute_values,
277     GESXmlFormatter * self, GError ** error)
278 {
279   const gchar *metadatas = NULL, *properties = NULL;
280   GESTimeline *timeline = GES_FORMATTER (self)->timeline;
281
282   if (!g_markup_collect_attributes (element_name, attribute_names,
283           attribute_values, error,
284           COLLECT_STR_OPT, "properties", &properties,
285           COLLECT_STR_OPT, "metadatas", &metadatas, G_MARKUP_COLLECT_INVALID))
286     return;
287
288   if (timeline == NULL)
289     return;
290
291   ges_base_xml_formatter_set_timeline_properties (GES_BASE_XML_FORMATTER (self),
292       timeline, properties, metadatas);
293 }
294
295 static inline void
296 _parse_asset (GMarkupParseContext * context, const gchar * element_name,
297     const gchar ** attribute_names, const gchar ** attribute_values,
298     GESXmlFormatter * self, GError ** error)
299 {
300   GType extractable_type;
301   const gchar *id, *extractable_type_name, *metadatas = NULL, *properties =
302       NULL, *proxy_id = NULL;
303
304   if (!g_markup_collect_attributes (element_name, attribute_names,
305           attribute_values, error, G_MARKUP_COLLECT_STRING, "id", &id,
306           G_MARKUP_COLLECT_STRING, "extractable-type-name",
307           &extractable_type_name,
308           COLLECT_STR_OPT, "properties", &properties,
309           COLLECT_STR_OPT, "metadatas", &metadatas,
310           COLLECT_STR_OPT, "proxy-id", &proxy_id, G_MARKUP_COLLECT_INVALID))
311     return;
312
313   extractable_type = g_type_from_name (extractable_type_name);
314   if (extractable_type == G_TYPE_NONE)
315     g_set_error (error, G_MARKUP_ERROR,
316         G_MARKUP_ERROR_INVALID_CONTENT,
317         "element '%s' invalid extractable_type %s'",
318         element_name, extractable_type_name);
319   else if (!g_type_is_a (extractable_type, GES_TYPE_EXTRACTABLE))
320     g_set_error (error, G_MARKUP_ERROR,
321         G_MARKUP_ERROR_INVALID_CONTENT,
322         "element '%s', %s not an extractable_type'",
323         element_name, extractable_type_name);
324   else {
325     GstStructure *props = NULL;
326     if (properties)
327       props = gst_structure_from_string (properties, NULL);
328
329     ges_base_xml_formatter_add_asset (GES_BASE_XML_FORMATTER (self), id,
330         extractable_type, props, metadatas, proxy_id, error);
331     if (props)
332       gst_structure_free (props);
333   }
334 }
335
336
337 static inline void
338 _parse_track (GMarkupParseContext * context, const gchar * element_name,
339     const gchar ** attribute_names, const gchar ** attribute_values,
340     GESXmlFormatter * self, GError ** error)
341 {
342   GstCaps *caps;
343   GESTrackType track_type;
344   GstStructure *props = NULL;
345   const gchar *strtrack_type, *strcaps, *strtrack_id, *metadatas =
346       NULL, *properties = NULL;
347
348   if (!g_markup_collect_attributes (element_name, attribute_names,
349           attribute_values, error,
350           G_MARKUP_COLLECT_STRING, "track-type", &strtrack_type,
351           G_MARKUP_COLLECT_STRING, "track-id", &strtrack_id,
352           COLLECT_STR_OPT, "properties", &properties,
353           COLLECT_STR_OPT, "metadatas", &metadatas,
354           G_MARKUP_COLLECT_STRING, "caps", &strcaps, G_MARKUP_COLLECT_INVALID))
355     return;
356
357   if ((caps = gst_caps_from_string (strcaps)) == NULL)
358     goto wrong_caps;
359
360   errno = 0;
361   track_type = g_ascii_strtoll (strtrack_type, NULL, 10);
362   if (errno)
363     goto convertion_failed;
364
365   if (properties) {
366     props = gst_structure_from_string (properties, NULL);
367   }
368
369   ges_base_xml_formatter_add_track (GES_BASE_XML_FORMATTER (self), track_type,
370       caps, strtrack_id, props, metadatas, error);
371
372   if (props)
373     gst_structure_free (props);
374
375   gst_caps_unref (caps);
376
377   return;
378
379 wrong_caps:
380   g_set_error (error, G_MARKUP_ERROR,
381       G_MARKUP_ERROR_INVALID_CONTENT,
382       "element '%s', Can not create caps: %s'", element_name, strcaps);
383   return;
384
385 convertion_failed:
386   gst_caps_unref (caps);
387   g_set_error (error, G_MARKUP_ERROR,
388       G_MARKUP_ERROR_INVALID_CONTENT,
389       "element '%s', Wrong property type, error: %s'", element_name,
390       g_strerror (errno));
391   return;
392
393 }
394
395 static inline void
396 _parse_layer (GMarkupParseContext * context, const gchar * element_name,
397     const gchar ** attribute_names, const gchar ** attribute_values,
398     GESXmlFormatter * self, GError ** error)
399 {
400   GstStructure *props = NULL;
401   guint priority;
402   GType extractable_type = G_TYPE_NONE;
403   const gchar *metadatas = NULL, *properties = NULL, *strprio = NULL,
404       *extractable_type_name;
405
406   if (!g_markup_collect_attributes (element_name, attribute_names,
407           attribute_values, error,
408           G_MARKUP_COLLECT_STRING, "priority", &strprio,
409           COLLECT_STR_OPT, "extractable-type-name", &extractable_type_name,
410           COLLECT_STR_OPT, "properties", &properties,
411           COLLECT_STR_OPT, "metadatas", &metadatas, G_MARKUP_COLLECT_INVALID))
412     return;
413
414   if (extractable_type_name) {
415     extractable_type = g_type_from_name (extractable_type_name);
416     if (extractable_type == G_TYPE_NONE) {
417       g_set_error (error, G_MARKUP_ERROR,
418           G_MARKUP_ERROR_INVALID_CONTENT,
419           "element '%s' invalid extractable_type %s'",
420           element_name, extractable_type_name);
421
422       return;
423     } else if (!g_type_is_a (extractable_type, GES_TYPE_EXTRACTABLE)) {
424       g_set_error (error, G_MARKUP_ERROR,
425           G_MARKUP_ERROR_INVALID_CONTENT,
426           "element '%s', %s not an extractable_type'",
427           element_name, extractable_type_name);
428
429       return;
430     }
431   }
432
433   if (properties) {
434     props = gst_structure_from_string (properties, NULL);
435     if (props == NULL)
436       goto wrong_properties;
437   }
438
439   errno = 0;
440   priority = g_ascii_strtoll (strprio, NULL, 10);
441   if (errno)
442     goto convertion_failed;
443
444   ges_base_xml_formatter_add_layer (GES_BASE_XML_FORMATTER (self),
445       extractable_type, priority, props, metadatas, error);
446
447 done:
448   if (props)
449     gst_structure_free (props);
450
451   return;
452
453 convertion_failed:
454   g_set_error (error, G_MARKUP_ERROR,
455       G_MARKUP_ERROR_INVALID_CONTENT,
456       "element '%s', Wrong property type, error: %s'", element_name,
457       g_strerror (errno));
458   goto done;
459
460 wrong_properties:
461   g_set_error (error, G_MARKUP_ERROR,
462       G_MARKUP_ERROR_INVALID_CONTENT,
463       "element '%s', wrong layer properties '%s', could no be deserialized",
464       element_name, properties);
465 }
466
467 static inline void
468 _parse_clip (GMarkupParseContext * context,
469     const gchar * element_name, const gchar ** attribute_names,
470     const gchar ** attribute_values, GESXmlFormatter * self, GError ** error)
471 {
472   GType type;
473   GstStructure *props = NULL, *children_props = NULL;
474   GESTrackType track_types;
475   GstClockTime start, inpoint = 0, duration, layer_prio;
476
477   const gchar *strid, *asset_id, *strstart, *strin, *strduration, *strrate,
478       *strtrack_types, *strtype, *metadatas = NULL, *properties =
479       NULL, *children_properties = NULL, *strlayer_prio;
480
481   if (!g_markup_collect_attributes (element_name, attribute_names,
482           attribute_values, error,
483           G_MARKUP_COLLECT_STRING, "id", &strid,
484           G_MARKUP_COLLECT_STRING, "type-name", &strtype,
485           G_MARKUP_COLLECT_STRING, "start", &strstart,
486           G_MARKUP_COLLECT_STRING, "duration", &strduration,
487           G_MARKUP_COLLECT_STRING, "asset-id", &asset_id,
488           G_MARKUP_COLLECT_STRING, "track-types", &strtrack_types,
489           G_MARKUP_COLLECT_STRING, "layer-priority", &strlayer_prio,
490           COLLECT_STR_OPT, "properties", &properties,
491           COLLECT_STR_OPT, "children-properties", &children_properties,
492           COLLECT_STR_OPT, "metadatas", &metadatas,
493           COLLECT_STR_OPT, "rate", &strrate,
494           COLLECT_STR_OPT, "inpoint", &strin, G_MARKUP_COLLECT_INVALID)) {
495     return;
496   }
497   type = g_type_from_name (strtype);
498   if (!g_type_is_a (type, GES_TYPE_CLIP))
499     goto wrong_type;
500
501   errno = 0;
502   track_types = g_ascii_strtoll (strtrack_types, NULL, 10);
503   if (errno)
504     goto convertion_failed;
505
506   layer_prio = g_ascii_strtoll (strlayer_prio, NULL, 10);
507   if (errno)
508     goto convertion_failed;
509
510   if (strin) {
511     inpoint = g_ascii_strtoull (strin, NULL, 10);
512     if (errno)
513       goto convertion_failed;
514   }
515
516   start = g_ascii_strtoull (strstart, NULL, 10);
517   if (errno)
518     goto convertion_failed;
519
520   duration = g_ascii_strtoull (strduration, NULL, 10);
521   if (errno)
522     goto convertion_failed;
523
524   if (properties) {
525     props = gst_structure_from_string (properties, NULL);
526     if (props == NULL)
527       goto wrong_properties;
528   }
529
530   if (children_properties) {
531     children_props = gst_structure_from_string (children_properties, NULL);
532     if (children_props == NULL)
533       goto wrong_children_properties;
534   }
535
536   ges_base_xml_formatter_add_clip (GES_BASE_XML_FORMATTER (self),
537       strid, asset_id, type, start, inpoint, duration, layer_prio,
538       track_types, props, children_props, metadatas, error);
539   if (props)
540     gst_structure_free (props);
541
542   return;
543
544 wrong_properties:
545   g_set_error (error, G_MARKUP_ERROR,
546       G_MARKUP_ERROR_INVALID_CONTENT,
547       "element '%s', Clip %s properties '%s', could no be deserialized",
548       element_name, asset_id, properties);
549   return;
550
551 wrong_children_properties:
552   g_set_error (error, G_MARKUP_ERROR,
553       G_MARKUP_ERROR_INVALID_CONTENT,
554       "element '%s', Clip %s children properties '%s', could no be deserialized",
555       element_name, asset_id, children_properties);
556   return;
557
558 convertion_failed:
559   g_set_error (error, G_MARKUP_ERROR,
560       G_MARKUP_ERROR_INVALID_CONTENT,
561       "element '%s', Wrong property type, error: %s'", element_name,
562       g_strerror (errno));
563   return;
564
565 wrong_type:
566   g_set_error (error, G_MARKUP_ERROR,
567       G_MARKUP_ERROR_INVALID_CONTENT,
568       "element '%s', %s not a GESClip'", element_name, strtype);
569 }
570
571 static inline void
572 _parse_binding (GMarkupParseContext * context, const gchar * element_name,
573     const gchar ** attribute_names, const gchar ** attribute_values,
574     GESXmlFormatter * self, GError ** error)
575 {
576   const gchar *type = NULL, *source_type = NULL, *timed_values =
577       NULL, *property_name = NULL, *mode = NULL, *track_id = NULL;
578   gchar **pairs, **tmp;
579   gchar *pair;
580   GSList *list = NULL;
581
582   if (!g_markup_collect_attributes (element_name, attribute_names,
583           attribute_values, error,
584           G_MARKUP_COLLECT_STRING, "type", &type,
585           G_MARKUP_COLLECT_STRING, "source_type", &source_type,
586           G_MARKUP_COLLECT_STRING, "property", &property_name,
587           G_MARKUP_COLLECT_STRING, "mode", &mode,
588           G_MARKUP_COLLECT_STRING, "track_id", &track_id,
589           G_MARKUP_COLLECT_STRING, "values", &timed_values,
590           G_MARKUP_COLLECT_INVALID)) {
591     return;
592   }
593
594   pairs = g_strsplit (timed_values, " ", 0);
595   for (tmp = pairs; tmp != NULL; tmp += 1) {
596     gchar **value_pair;
597
598     pair = *tmp;
599     if (pair == NULL)
600       break;
601     if (strlen (pair)) {
602       GstTimedValue *value;
603
604       value = g_slice_new (GstTimedValue);
605       value_pair = g_strsplit (pair, ":", 0);
606       value->timestamp = g_ascii_strtoull (value_pair[0], NULL, 10);
607       value->value = g_ascii_strtod (value_pair[1], NULL);
608       list = g_slist_append (list, value);
609       g_strfreev (value_pair);
610     }
611   }
612
613   g_strfreev (pairs);
614
615   ges_base_xml_formatter_add_control_binding (GES_BASE_XML_FORMATTER (self),
616       type,
617       source_type,
618       property_name, (gint) g_ascii_strtoll (mode, NULL, 10), track_id, list);
619 }
620
621 static inline void
622 _parse_source (GMarkupParseContext * context, const gchar * element_name,
623     const gchar ** attribute_names, const gchar ** attribute_values,
624     GESXmlFormatter * self, GError ** error)
625 {
626   GstStructure *children_props = NULL;
627   const gchar *track_id = NULL, *children_properties = NULL;
628
629   if (!g_markup_collect_attributes (element_name, attribute_names,
630           attribute_values, error,
631           G_MARKUP_COLLECT_STRING, "track-id", &track_id,
632           COLLECT_STR_OPT, "children-properties", &children_properties,
633           G_MARKUP_COLLECT_INVALID)) {
634     return;
635   }
636
637   if (children_properties) {
638     children_props = gst_structure_from_string (children_properties, NULL);
639     if (children_props == NULL)
640       goto wrong_children_properties;
641   }
642
643   ges_base_xml_formatter_add_source (GES_BASE_XML_FORMATTER (self), track_id,
644       children_props);
645
646   if (children_props)
647     gst_structure_free (children_props);
648
649   return;
650
651 wrong_children_properties:
652   g_set_error (error, G_MARKUP_ERROR,
653       G_MARKUP_ERROR_INVALID_CONTENT,
654       "element '%s', children properties '%s', could no be deserialized",
655       element_name, children_properties);
656 }
657
658 static inline void
659 _parse_effect (GMarkupParseContext * context, const gchar * element_name,
660     const gchar ** attribute_names, const gchar ** attribute_values,
661     GESXmlFormatter * self, GError ** error)
662 {
663   GType type;
664
665   GstStructure *children_props = NULL, *props = NULL;
666   const gchar *asset_id = NULL, *strtype = NULL, *track_id =
667       NULL, *metadatas = NULL, *properties = NULL, *track_type = NULL,
668       *children_properties = NULL, *clip_id;
669
670   if (!g_markup_collect_attributes (element_name, attribute_names,
671           attribute_values, error,
672           COLLECT_STR_OPT, "metadatas", &metadatas,
673           G_MARKUP_COLLECT_STRING, "asset-id", &asset_id,
674           G_MARKUP_COLLECT_STRING, "clip-id", &clip_id,
675           G_MARKUP_COLLECT_STRING, "type-name", &strtype,
676           G_MARKUP_COLLECT_STRING, "track-id", &track_id,
677           COLLECT_STR_OPT, "children-properties", &children_properties,
678           COLLECT_STR_OPT, "track-type", &track_type,
679           COLLECT_STR_OPT, "properties", &properties,
680           G_MARKUP_COLLECT_INVALID)) {
681     return;
682   }
683
684   type = g_type_from_name (strtype);
685   if (!g_type_is_a (type, GES_TYPE_BASE_EFFECT))
686     goto wrong_type;
687
688   if (children_properties) {
689     children_props = gst_structure_from_string (children_properties, NULL);
690     if (children_props == NULL)
691       goto wrong_children_properties;
692   }
693
694   if (properties) {
695     props = gst_structure_from_string (properties, NULL);
696     if (props == NULL)
697       goto wrong_properties;
698   }
699
700   ges_base_xml_formatter_add_track_element (GES_BASE_XML_FORMATTER (self),
701       type, asset_id, track_id, clip_id, children_props, props, metadatas,
702       error);
703
704 out:
705
706   if (props)
707     gst_structure_free (props);
708   if (children_props)
709     gst_structure_free (children_props);
710
711   return;
712
713 wrong_properties:
714   g_set_error (error, G_MARKUP_ERROR,
715       G_MARKUP_ERROR_INVALID_CONTENT,
716       "element '%s', Effect %s properties '%s', could no be deserialized",
717       element_name, asset_id, properties);
718   goto out;
719
720 wrong_children_properties:
721   g_set_error (error, G_MARKUP_ERROR,
722       G_MARKUP_ERROR_INVALID_CONTENT,
723       "element '%s', Effect %s children properties '%s', could no be deserialized",
724       element_name, asset_id, children_properties);
725   goto out;
726
727 wrong_type:
728   g_set_error (error, G_MARKUP_ERROR,
729       G_MARKUP_ERROR_INVALID_CONTENT,
730       "element '%s', %s not a GESBaseEffect'", element_name, strtype);
731 }
732
733
734 static inline void
735 _parse_group (GMarkupParseContext * context, const gchar * element_name,
736     const gchar ** attribute_names, const gchar ** attribute_values,
737     GESXmlFormatter * self, GError ** error)
738 {
739   const gchar *id, *properties, *metadatas = NULL;
740
741   if (!g_markup_collect_attributes (element_name, attribute_names,
742           attribute_values, error,
743           G_MARKUP_COLLECT_STRING, "id", &id,
744           G_MARKUP_COLLECT_STRING, "properties", &properties,
745           COLLECT_STR_OPT, "metadatas", &metadatas, G_MARKUP_COLLECT_INVALID)) {
746     return;
747   }
748
749   ges_base_xml_formatter_add_group (GES_BASE_XML_FORMATTER (self), id,
750       properties, metadatas);
751 }
752
753 static inline void
754 _parse_group_child (GMarkupParseContext * context, const gchar * element_name,
755     const gchar ** attribute_names, const gchar ** attribute_values,
756     GESXmlFormatter * self, GError ** error)
757 {
758   const gchar *child_id, *name;
759
760   if (!g_markup_collect_attributes (element_name, attribute_names,
761           attribute_values, error,
762           G_MARKUP_COLLECT_STRING, "id", &child_id,
763           G_MARKUP_COLLECT_STRING, "name", &name, G_MARKUP_COLLECT_INVALID)) {
764     return;
765   }
766
767   ges_base_xml_formatter_last_group_add_child (GES_BASE_XML_FORMATTER (self),
768       child_id, name);
769 }
770
771 static void
772 _parse_element_start (GMarkupParseContext * context, const gchar * element_name,
773     const gchar ** attribute_names, const gchar ** attribute_values,
774     gpointer self, GError ** error)
775 {
776   GESXmlFormatterPrivate *priv = _GET_PRIV (self);
777
778   if (!G_UNLIKELY (priv->ges_opened))
779     _parse_ges_element (context, element_name, attribute_names,
780         attribute_values, self, error);
781   else if (!G_UNLIKELY (priv->project_opened))
782     _parse_project (context, element_name, attribute_names, attribute_values,
783         self, error);
784   else if (g_strcmp0 (element_name, "encoding-profile") == 0)
785     _parse_encoding_profile (context, element_name, attribute_names,
786         attribute_values, self, error);
787   else if (g_strcmp0 (element_name, "stream-profile") == 0)
788     _parse_stream_profile (context, element_name, attribute_names,
789         attribute_values, self, error);
790   else if (g_strcmp0 (element_name, "timeline") == 0)
791     _parse_timeline (context, element_name, attribute_names, attribute_values,
792         self, error);
793   else if (g_strcmp0 (element_name, "asset") == 0)
794     _parse_asset (context, element_name, attribute_names, attribute_values,
795         self, error);
796   else if (g_strcmp0 (element_name, "track") == 0)
797     _parse_track (context, element_name, attribute_names,
798         attribute_values, self, error);
799   else if (g_strcmp0 (element_name, "layer") == 0)
800     _parse_layer (context, element_name, attribute_names,
801         attribute_values, self, error);
802   else if (g_strcmp0 (element_name, "clip") == 0)
803     _parse_clip (context, element_name, attribute_names,
804         attribute_values, self, error);
805   else if (g_strcmp0 (element_name, "source") == 0)
806     _parse_source (context, element_name, attribute_names,
807         attribute_values, self, error);
808   else if (g_strcmp0 (element_name, "effect") == 0)
809     _parse_effect (context, element_name, attribute_names,
810         attribute_values, self, error);
811   else if (g_strcmp0 (element_name, "binding") == 0)
812     _parse_binding (context, element_name, attribute_names,
813         attribute_values, self, error);
814   else if (g_strcmp0 (element_name, "group") == 0)
815     _parse_group (context, element_name, attribute_names,
816         attribute_values, self, error);
817   else if (g_strcmp0 (element_name, "child") == 0)
818     _parse_group_child (context, element_name, attribute_names,
819         attribute_values, self, error);
820   else
821     GST_LOG_OBJECT (self, "Element %s not handled", element_name);
822 }
823
824 static void
825 _parse_element_end (GMarkupParseContext * context,
826     const gchar * element_name, gpointer self, GError ** error)
827 {
828   /*GESXmlFormatterPrivate *priv = _GET_PRIV (self); */
829   if (g_strcmp0 (element_name, "ges") == 0 && GES_FORMATTER (self)->project) {
830     gchar *version = g_strdup_printf ("%d.%d",
831         API_VERSION, GES_XML_FORMATTER (self)->priv->min_version);
832
833     ges_meta_container_set_string (GES_META_CONTAINER (GES_FORMATTER
834             (self)->project), GES_META_FORMAT_VERSION, version);
835
836     g_free (version);
837   }
838 }
839
840 static void
841 _error_parsing (GMarkupParseContext * context, GError * error,
842     gpointer user_data)
843 {
844   GST_WARNING ("Error occurred when parsing %s", error->message);
845 }
846
847 /***********************************************
848  *                                             *
849  *            Saving implementation            *
850  *                                             *
851  ***********************************************/
852
853 /* XML writting utils */
854 static inline void
855 append_escaped (GString * str, gchar * tmpstr)
856 {
857   g_string_append (str, tmpstr);
858   g_free (tmpstr);
859 }
860
861 static inline gboolean
862 _can_serialize_spec (GParamSpec * spec)
863 {
864   if (!(spec->flags & G_PARAM_WRITABLE)) {
865     GST_DEBUG ("%s from %s is not writable",
866         spec->name, g_type_name (spec->owner_type));
867
868     return FALSE;
869   } else if (spec->flags & G_PARAM_CONSTRUCT_ONLY) {
870     GST_DEBUG ("%s from %s is construct only",
871         spec->name, g_type_name (spec->owner_type));
872
873     return FALSE;
874   } else if (spec->flags & GES_PARAM_NO_SERIALIZATION &&
875       g_type_is_a (spec->owner_type, GES_TYPE_TIMELINE_ELEMENT)) {
876     GST_DEBUG ("%s from %s is set as GES_PARAM_NO_SERIALIZATION",
877         spec->name, g_type_name (spec->owner_type));
878
879     return FALSE;
880   } else if (g_type_is_a (G_PARAM_SPEC_VALUE_TYPE (spec), G_TYPE_OBJECT)) {
881     GST_DEBUG ("%s from %s contains GObject, can't serialize that.",
882         spec->name, g_type_name (spec->owner_type));
883
884     return FALSE;
885   } else if ((g_type_is_a (spec->owner_type, GST_TYPE_OBJECT) &&
886           !g_strcmp0 (spec->name, "name"))) {
887
888     GST_DEBUG ("We do not want to serialize the name of GstObjects.");
889     return FALSE;
890   } else if (G_PARAM_SPEC_VALUE_TYPE (spec) == G_TYPE_GTYPE) {
891     GST_DEBUG ("%s from %s contains a GType, can't serialize.",
892         spec->name, g_type_name (spec->owner_type));
893     return FALSE;
894   }
895
896   return TRUE;
897 }
898
899 static inline void
900 _init_value_from_spec_for_serialization (GValue * value, GParamSpec * spec)
901 {
902
903   if (g_type_is_a (spec->value_type, G_TYPE_ENUM) ||
904       g_type_is_a (spec->value_type, G_TYPE_FLAGS))
905     g_value_init (value, G_TYPE_INT);
906   else
907     g_value_init (value, spec->value_type);
908 }
909
910 static gchar *
911 _serialize_properties (GObject * object, const gchar * fieldname, ...)
912 {
913   gchar *ret;
914   guint n_props, j;
915   GParamSpec *spec, **pspecs;
916   GObjectClass *class = G_OBJECT_GET_CLASS (object);
917   GstStructure *structure = gst_structure_new_empty ("properties");
918
919   pspecs = g_object_class_list_properties (class, &n_props);
920   for (j = 0; j < n_props; j++) {
921     GValue val = { 0 };
922
923     spec = pspecs[j];
924     if (spec->value_type == GST_TYPE_CAPS) {
925       GstCaps *caps;
926       gchar *caps_str;
927
928       g_object_get (object, spec->name, &caps, NULL);
929       caps_str = gst_caps_to_string (caps);
930       gst_structure_set (structure, spec->name, G_TYPE_STRING, caps_str, NULL);
931       g_free (caps_str);
932     } else if (_can_serialize_spec (spec)) {
933       _init_value_from_spec_for_serialization (&val, spec);
934       g_object_get_property (object, spec->name, &val);
935       gst_structure_set_value (structure, spec->name, &val);
936       g_value_unset (&val);
937     }
938   }
939   g_free (pspecs);
940
941   if (fieldname) {
942     va_list varargs;
943     va_start (varargs, fieldname);
944     gst_structure_remove_fields_valist (structure, fieldname, varargs);
945     va_end (varargs);
946   }
947
948   ret = gst_structure_to_string (structure);
949   gst_structure_free (structure);
950
951   return ret;
952 }
953
954 static inline void
955 _save_assets (GESXmlFormatter * self, GString * str, GESProject * project)
956 {
957   char *properties, *metas;
958   GESAsset *asset, *proxy;
959   GList *assets, *tmp;
960
961   assets = ges_project_list_assets (project, GES_TYPE_EXTRACTABLE);
962   for (tmp = assets; tmp; tmp = tmp->next) {
963     asset = GES_ASSET (tmp->data);
964     properties = _serialize_properties (G_OBJECT (asset), NULL);
965     metas = ges_meta_container_metas_to_string (GES_META_CONTAINER (asset));
966     append_escaped (str,
967         g_markup_printf_escaped
968         ("      <asset id='%s' extractable-type-name='%s' properties='%s' metadatas='%s' ",
969             ges_asset_get_id (asset),
970             g_type_name (ges_asset_get_extractable_type (asset)), properties,
971             metas));
972
973     /*TODO Save the whole list of proxies */
974     proxy = ges_asset_get_proxy (asset);
975     if (proxy) {
976       append_escaped (str, g_markup_printf_escaped (" proxy-id='%s' ",
977               ges_asset_get_id (proxy)));
978
979       if (!g_list_find (assets, proxy)) {
980         assets = g_list_append (assets, gst_object_ref (proxy));
981
982         if (!tmp->next)
983           tmp->next = g_list_last (assets);
984       }
985
986       self->priv->min_version = MAX (self->priv->min_version, 3);
987     }
988     g_string_append (str, "/>\n");
989     g_free (properties);
990     g_free (metas);
991   }
992   g_list_free_full (assets, gst_object_unref);
993 }
994
995 static inline void
996 _save_tracks (GESXmlFormatter * self, GString * str, GESTimeline * timeline)
997 {
998   gchar *strtmp, *metas;
999   GESTrack *track;
1000   GList *tmp, *tracks;
1001   char *properties;
1002
1003   guint nb_tracks = 0;
1004
1005   tracks = ges_timeline_get_tracks (timeline);
1006   for (tmp = tracks; tmp; tmp = tmp->next) {
1007     track = GES_TRACK (tmp->data);
1008     properties = _serialize_properties (G_OBJECT (track), NULL);
1009     strtmp = gst_caps_to_string (ges_track_get_caps (track));
1010     metas = ges_meta_container_metas_to_string (GES_META_CONTAINER (track));
1011     append_escaped (str,
1012         g_markup_printf_escaped
1013         ("      <track caps='%s' track-type='%i' track-id='%i' properties='%s' metadatas='%s'/>\n",
1014             strtmp, track->type, nb_tracks++, properties, metas));
1015     g_free (strtmp);
1016     g_free (metas);
1017     g_free (properties);
1018   }
1019   g_list_free_full (tracks, gst_object_unref);
1020 }
1021
1022 static inline void
1023 _save_children_properties (GString * str, GESTimelineElement * element)
1024 {
1025   GstStructure *structure;
1026   GParamSpec **pspecs, *spec;
1027   guint i, n_props;
1028   gchar *struct_str;
1029
1030   pspecs = ges_timeline_element_list_children_properties (element, &n_props);
1031
1032   structure = gst_structure_new_empty ("properties");
1033   for (i = 0; i < n_props; i++) {
1034     GValue val = { 0 };
1035     spec = pspecs[i];
1036
1037     if (_can_serialize_spec (spec)) {
1038       gchar *spec_name =
1039           g_strdup_printf ("%s::%s", g_type_name (spec->owner_type),
1040           spec->name);
1041
1042       _init_value_from_spec_for_serialization (&val, spec);
1043       ges_timeline_element_get_child_property_by_pspec (element, spec, &val);
1044       gst_structure_set_value (structure, spec_name, &val);
1045
1046       g_free (spec_name);
1047       g_value_unset (&val);
1048     }
1049     g_param_spec_unref (spec);
1050   }
1051   g_free (pspecs);
1052
1053   struct_str = gst_structure_to_string (structure);
1054   append_escaped (str,
1055       g_markup_printf_escaped (" children-properties='%s'", struct_str));
1056   gst_structure_free (structure);
1057   g_free (struct_str);
1058 }
1059
1060 /* TODO : Use this function for every track element with controllable properties */
1061 static inline void
1062 _save_keyframes (GString * str, GESTrackElement * trackelement, gint index)
1063 {
1064   GHashTable *bindings_hashtable;
1065   GHashTableIter iter;
1066   gpointer key, value;
1067
1068   bindings_hashtable =
1069       ges_track_element_get_all_control_bindings (trackelement);
1070
1071   g_hash_table_iter_init (&iter, bindings_hashtable);
1072
1073   /* We iterate over the bindings, and save the timed values */
1074   while (g_hash_table_iter_next (&iter, &key, &value)) {
1075     if (GST_IS_DIRECT_CONTROL_BINDING ((GstControlBinding *) value)) {
1076       GstControlSource *source;
1077       gboolean absolute = FALSE;
1078       GstDirectControlBinding *binding;
1079
1080       binding = (GstDirectControlBinding *) value;
1081
1082       g_object_get (binding, "control-source", &source,
1083           "absolute", &absolute, NULL);
1084
1085       if (GST_IS_INTERPOLATION_CONTROL_SOURCE (source)) {
1086         GList *timed_values, *tmp;
1087         GstInterpolationMode mode;
1088
1089         append_escaped (str,
1090             g_markup_printf_escaped
1091             ("            <binding type='%s' source_type='interpolation' property='%s'",
1092                 absolute ? "direct-absolute" : "direct", (gchar *) key));
1093
1094         g_object_get (source, "mode", &mode, NULL);
1095         append_escaped (str, g_markup_printf_escaped (" mode='%d'", mode));
1096         append_escaped (str, g_markup_printf_escaped (" track_id='%d'", index));
1097         append_escaped (str, g_markup_printf_escaped (" values ='"));
1098         timed_values =
1099             gst_timed_value_control_source_get_all
1100             (GST_TIMED_VALUE_CONTROL_SOURCE (source));
1101         for (tmp = timed_values; tmp; tmp = tmp->next) {
1102           gchar strbuf[G_ASCII_DTOSTR_BUF_SIZE];
1103           GstTimedValue *value;
1104
1105           value = (GstTimedValue *) tmp->data;
1106           append_escaped (str, g_markup_printf_escaped (" %" G_GUINT64_FORMAT
1107                   ":%s ", value->timestamp, g_ascii_dtostr (strbuf,
1108                       G_ASCII_DTOSTR_BUF_SIZE, value->value)));
1109         }
1110         append_escaped (str, g_markup_printf_escaped ("'/>\n"));
1111       } else
1112         GST_DEBUG ("control source not in [interpolation]");
1113     } else
1114       GST_DEBUG ("Binding type not in [direct, direct-absolute]");
1115   }
1116 }
1117
1118 static inline void
1119 _save_effect (GString * str, guint clip_id, GESTrackElement * trackelement,
1120     GESTimeline * timeline)
1121 {
1122   GESTrack *tck;
1123   GList *tmp, *tracks;
1124   gchar *properties, *metas;
1125   guint track_id = 0;
1126   gboolean serialize;
1127   gchar *extractable_id;
1128
1129   g_object_get (trackelement, "serialize", &serialize, NULL);
1130   if (!serialize) {
1131
1132     GST_DEBUG_OBJECT (trackelement, "Should not be serialized");
1133
1134     return;
1135   }
1136
1137   tck = ges_track_element_get_track (trackelement);
1138   if (tck == NULL) {
1139     GST_WARNING_OBJECT (trackelement, " Not in any track, can not save it");
1140
1141     return;
1142   }
1143
1144   tracks = ges_timeline_get_tracks (timeline);
1145   for (tmp = tracks; tmp; tmp = tmp->next) {
1146     if (tmp->data == tck)
1147       break;
1148     track_id++;
1149   }
1150   g_list_free_full (tracks, gst_object_unref);
1151
1152   properties = _serialize_properties (G_OBJECT (trackelement), "start",
1153       "in-point", "duration", "locked", "max-duration", "name", "priority",
1154       NULL);
1155   metas =
1156       ges_meta_container_metas_to_string (GES_META_CONTAINER (trackelement));
1157   extractable_id = ges_extractable_get_id (GES_EXTRACTABLE (trackelement));
1158   append_escaped (str,
1159       g_markup_printf_escaped ("          <effect asset-id='%s' clip-id='%u'"
1160           " type-name='%s' track-type='%i' track-id='%i' properties='%s' metadatas='%s'",
1161           extractable_id, clip_id,
1162           g_type_name (G_OBJECT_TYPE (trackelement)), tck->type, track_id,
1163           properties, metas));
1164   g_free (extractable_id);
1165   g_free (properties);
1166   g_free (metas);
1167
1168   _save_children_properties (str, GES_TIMELINE_ELEMENT (trackelement));
1169   append_escaped (str, g_markup_printf_escaped (">\n"));
1170
1171   _save_keyframes (str, trackelement, -1);
1172
1173   append_escaped (str, g_markup_printf_escaped ("          </effect>\n"));
1174 }
1175
1176 static inline void
1177 _save_layers (GESXmlFormatter * self, GString * str, GESTimeline * timeline)
1178 {
1179   gchar *properties, *metas;
1180   GESLayer *layer;
1181   GESClip *clip;
1182   GList *tmplayer, *tmpclip, *clips;
1183   GESXmlFormatterPrivate *priv = self->priv;
1184
1185   for (tmplayer = timeline->layers; tmplayer; tmplayer = tmplayer->next) {
1186     guint priority;
1187     layer = GES_LAYER (tmplayer->data);
1188
1189     priority = ges_layer_get_priority (layer);
1190     properties = _serialize_properties (G_OBJECT (layer), "priority", NULL);
1191     metas = ges_meta_container_metas_to_string (GES_META_CONTAINER (layer));
1192     append_escaped (str,
1193         g_markup_printf_escaped
1194         ("      <layer priority='%i' properties='%s' metadatas='%s'>\n",
1195             priority, properties, metas));
1196     g_free (properties);
1197     g_free (metas);
1198
1199     clips = ges_layer_get_clips (layer);
1200     for (tmpclip = clips; tmpclip; tmpclip = tmpclip->next) {
1201       GList *effects, *tmpeffect;
1202       GList *tmptrackelement;
1203       GList *tracks;
1204       gboolean serialize;
1205       gchar *extractable_id;
1206
1207       clip = GES_CLIP (tmpclip->data);
1208
1209       g_object_get (clip, "serialize", &serialize, NULL);
1210       if (!serialize) {
1211         GST_DEBUG_OBJECT (clip, "Should not be serialized");
1212         continue;
1213       }
1214
1215       /* We escape all mandatrorry properties that are handled sparetely
1216        * and vtype for StandarTransition as it is the asset ID */
1217       properties = _serialize_properties (G_OBJECT (clip),
1218           "supported-formats", "rate", "in-point", "start", "duration",
1219           "max-duration", "priority", "vtype", "uri", NULL);
1220       extractable_id = ges_extractable_get_id (GES_EXTRACTABLE (clip));
1221       metas = ges_meta_container_metas_to_string (GES_META_CONTAINER (clip));
1222       append_escaped (str,
1223           g_markup_printf_escaped ("        <clip id='%i' asset-id='%s'"
1224               " type-name='%s' layer-priority='%i' track-types='%i' start='%"
1225               G_GUINT64_FORMAT "' duration='%" G_GUINT64_FORMAT "' inpoint='%"
1226               G_GUINT64_FORMAT "' rate='%d' properties='%s' metadatas='%s'",
1227               priv->nbelements, extractable_id,
1228               g_type_name (G_OBJECT_TYPE (clip)), priority,
1229               ges_clip_get_supported_formats (clip), _START (clip),
1230               _DURATION (clip), _INPOINT (clip), 0, properties, metas));
1231
1232       if (GES_IS_TRANSITION_CLIP (clip)) {
1233         _save_children_properties (str, GES_TIMELINE_ELEMENT (clip));
1234         self->priv->min_version = MAX (self->priv->min_version, 4);
1235       }
1236       g_string_append (str, ">\n");
1237
1238       g_free (extractable_id);
1239       g_free (properties);
1240
1241       g_hash_table_insert (self->priv->element_id, clip,
1242           GINT_TO_POINTER (priv->nbelements));
1243
1244
1245       /* Effects must always be serialized in the right priority order.
1246        * List order is guaranteed by the fact that ges_clip_get_top_effects
1247        * sorts the effects. */
1248       effects = ges_clip_get_top_effects (clip);
1249       for (tmpeffect = effects; tmpeffect; tmpeffect = tmpeffect->next) {
1250         _save_effect (str, priv->nbelements,
1251             GES_TRACK_ELEMENT (tmpeffect->data), timeline);
1252       }
1253
1254
1255       tracks = ges_timeline_get_tracks (timeline);
1256
1257       for (tmptrackelement = GES_CONTAINER_CHILDREN (clip); tmptrackelement;
1258           tmptrackelement = tmptrackelement->next) {
1259         gint index;
1260         gboolean serialize;
1261
1262         if (!GES_IS_SOURCE (tmptrackelement->data))
1263           continue;
1264
1265         g_object_get (tmptrackelement->data, "serialize", &serialize, NULL);
1266         if (!serialize) {
1267           GST_DEBUG_OBJECT (tmptrackelement->data, "Should not be serialized");
1268           continue;
1269         }
1270
1271         index =
1272             g_list_index (tracks,
1273             ges_track_element_get_track (tmptrackelement->data));
1274         append_escaped (str,
1275             g_markup_printf_escaped ("          <source track-id='%i'", index));
1276         _save_children_properties (str, tmptrackelement->data);
1277         append_escaped (str, g_markup_printf_escaped (">\n"));
1278         _save_keyframes (str, tmptrackelement->data, index);
1279         append_escaped (str, g_markup_printf_escaped ("          </source>\n"));
1280       }
1281
1282       g_list_free_full (tracks, gst_object_unref);
1283
1284       g_string_append (str, "        </clip>\n");
1285
1286       priv->nbelements++;
1287     }
1288     g_list_free_full (clips, (GDestroyNotify) gst_object_unref);
1289     g_string_append (str, "      </layer>\n");
1290   }
1291 }
1292
1293 static void
1294 _save_group (GESXmlFormatter * self, GString * str, GList ** seen_groups,
1295     GESGroup * group)
1296 {
1297   GList *tmp;
1298   gboolean serialize;
1299   gchar *properties, *metadatas;
1300
1301   g_object_get (group, "serialize", &serialize, NULL);
1302   if (!serialize) {
1303
1304     GST_DEBUG_OBJECT (group, "Should not be serialized");
1305
1306     return;
1307   }
1308
1309   if (g_list_find (*seen_groups, group)) {
1310     GST_DEBUG_OBJECT (group, "Already serialized");
1311
1312     return;
1313   }
1314
1315   *seen_groups = g_list_prepend (*seen_groups, group);
1316   for (tmp = GES_CONTAINER_CHILDREN (group); tmp; tmp = tmp->next) {
1317     if (GES_IS_GROUP (tmp->data)) {
1318       _save_group (self, str, seen_groups,
1319           GES_GROUP (GES_TIMELINE_ELEMENT (tmp->data)));
1320     }
1321   }
1322
1323   properties = _serialize_properties (G_OBJECT (group), NULL);
1324
1325   metadatas = ges_meta_container_metas_to_string (GES_META_CONTAINER (group));
1326   self->priv->min_version = MAX (self->priv->min_version, 5);
1327
1328   g_string_append_printf (str,
1329       "        <group id='%d' properties='%s' metadatas='%s'>\n",
1330       self->priv->nbelements, properties, metadatas);
1331   g_free (properties);
1332   g_free (metadatas);
1333   g_hash_table_insert (self->priv->element_id, group,
1334       GINT_TO_POINTER (self->priv->nbelements));
1335   self->priv->nbelements++;
1336
1337   for (tmp = GES_CONTAINER_CHILDREN (group); tmp; tmp = tmp->next) {
1338     gint id = GPOINTER_TO_INT (g_hash_table_lookup (self->priv->element_id,
1339             tmp->data));
1340
1341     g_string_append_printf (str, "          <child id='%d' name='%s'/>\n", id,
1342         GES_TIMELINE_ELEMENT_NAME (tmp->data));
1343   }
1344   g_string_append (str, "        </group>\n");
1345 }
1346
1347 static void
1348 _save_groups (GESXmlFormatter * self, GString * str, GESTimeline * timeline)
1349 {
1350   GList *tmp;
1351   GList *seen_groups = NULL;
1352
1353   g_string_append (str, "      <groups>\n");
1354   for (tmp = ges_timeline_get_groups (timeline); tmp; tmp = tmp->next) {
1355     _save_group (self, str, &seen_groups, tmp->data);
1356   }
1357   g_list_free (seen_groups);
1358   g_string_append (str, "      </groups>\n");
1359 }
1360
1361 static inline void
1362 _save_timeline (GESXmlFormatter * self, GString * str, GESTimeline * timeline)
1363 {
1364   gchar *properties = NULL, *metas = NULL;
1365
1366   properties = _serialize_properties (G_OBJECT (timeline), "update", "name",
1367       "async-handling", "message-forward", NULL);
1368
1369   ges_meta_container_set_uint64 (GES_META_CONTAINER (timeline), "duration",
1370       ges_timeline_get_duration (timeline));
1371   metas = ges_meta_container_metas_to_string (GES_META_CONTAINER (timeline));
1372   append_escaped (str,
1373       g_markup_printf_escaped
1374       ("    <timeline properties='%s' metadatas='%s'>\n", properties, metas));
1375
1376   _save_tracks (self, str, timeline);
1377   _save_layers (self, str, timeline);
1378   _save_groups (self, str, timeline);
1379
1380   g_string_append (str, "    </timeline>\n");
1381
1382   g_free (properties);
1383   g_free (metas);
1384 }
1385
1386 static void
1387 _save_stream_profiles (GESXmlFormatter * self, GString * str,
1388     GstEncodingProfile * sprof, const gchar * profilename, guint id)
1389 {
1390   gchar *tmpc;
1391   GstCaps *tmpcaps;
1392   const gchar *preset, *preset_name, *name, *description;
1393
1394   append_escaped (str,
1395       g_markup_printf_escaped
1396       ("        <stream-profile parent='%s' id='%d' type='%s' "
1397           "presence='%d' ", profilename, id,
1398           gst_encoding_profile_get_type_nick (sprof),
1399           gst_encoding_profile_get_presence (sprof)));
1400
1401   if (!gst_encoding_profile_is_enabled (sprof)) {
1402     append_escaped (str, g_strdup ("enabled='0' "));
1403
1404     self->priv->min_version = MAX (self->priv->min_version, 2);
1405   }
1406
1407   tmpcaps = gst_encoding_profile_get_format (sprof);
1408   if (tmpcaps) {
1409     tmpc = gst_caps_to_string (tmpcaps);
1410     append_escaped (str, g_markup_printf_escaped ("format='%s' ", tmpc));
1411     gst_caps_unref (tmpcaps);
1412     g_free (tmpc);
1413   }
1414
1415   name = gst_encoding_profile_get_name (sprof);
1416   if (name)
1417     append_escaped (str, g_markup_printf_escaped ("name='%s' ", name));
1418
1419   description = gst_encoding_profile_get_description (sprof);
1420   if (description)
1421     append_escaped (str, g_markup_printf_escaped ("description='%s' ",
1422             description));
1423
1424   preset = gst_encoding_profile_get_preset (sprof);
1425   if (preset) {
1426     GstElement *encoder;
1427
1428     append_escaped (str, g_markup_printf_escaped ("preset='%s' ", preset));
1429
1430     encoder = get_element_for_encoding_profile (sprof,
1431         GST_ELEMENT_FACTORY_TYPE_ENCODER);
1432     if (encoder) {
1433       if (GST_IS_PRESET (encoder) &&
1434           gst_preset_load_preset (GST_PRESET (encoder), preset)) {
1435
1436         gchar *settings = _serialize_properties (G_OBJECT (encoder), NULL);
1437         append_escaped (str,
1438             g_markup_printf_escaped ("preset-properties='%s' ", settings));
1439         g_free (settings);
1440       }
1441       gst_object_unref (encoder);
1442     }
1443   }
1444
1445   preset_name = gst_encoding_profile_get_preset_name (sprof);
1446   if (preset_name)
1447     append_escaped (str, g_markup_printf_escaped ("preset-name='%s' ",
1448             preset_name));
1449
1450   tmpcaps = gst_encoding_profile_get_restriction (sprof);
1451   if (tmpcaps) {
1452     tmpc = gst_caps_to_string (tmpcaps);
1453     append_escaped (str, g_markup_printf_escaped ("restriction='%s' ", tmpc));
1454     gst_caps_unref (tmpcaps);
1455     g_free (tmpc);
1456   }
1457
1458   if (GST_IS_ENCODING_VIDEO_PROFILE (sprof)) {
1459     GstEncodingVideoProfile *vp = (GstEncodingVideoProfile *) sprof;
1460
1461     append_escaped (str,
1462         g_markup_printf_escaped ("pass='%d' variableframerate='%i' ",
1463             gst_encoding_video_profile_get_pass (vp),
1464             gst_encoding_video_profile_get_variableframerate (vp)));
1465   }
1466
1467   g_string_append (str, "/>\n");
1468 }
1469
1470 static inline void
1471 _save_encoding_profiles (GESXmlFormatter * self, GString * str,
1472     GESProject * project)
1473 {
1474   GstCaps *profformat;
1475   const gchar *profname, *profdesc, *profpreset, *proftype, *profpresetname;
1476
1477   const GList *tmp;
1478   GList *profiles = g_list_reverse (g_list_copy ((GList *)
1479           ges_project_list_encoding_profiles (project)));
1480
1481   for (tmp = profiles; tmp; tmp = tmp->next) {
1482     GstEncodingProfile *prof = GST_ENCODING_PROFILE (tmp->data);
1483
1484     profname = gst_encoding_profile_get_name (prof);
1485     profdesc = gst_encoding_profile_get_description (prof);
1486     profpreset = gst_encoding_profile_get_preset (prof);
1487     profpresetname = gst_encoding_profile_get_preset_name (prof);
1488     proftype = gst_encoding_profile_get_type_nick (prof);
1489
1490     append_escaped (str,
1491         g_markup_printf_escaped
1492         ("      <encoding-profile name='%s' description='%s' type='%s' ",
1493             profname, profdesc, proftype));
1494
1495     if (profpreset) {
1496       GstElement *element;
1497
1498       append_escaped (str, g_markup_printf_escaped ("preset='%s' ",
1499               profpreset));
1500
1501       if (GST_IS_ENCODING_CONTAINER_PROFILE (prof)) {
1502         element = get_element_for_encoding_profile (prof,
1503             GST_ELEMENT_FACTORY_TYPE_MUXER);
1504       } else {
1505         element = get_element_for_encoding_profile (prof,
1506             GST_ELEMENT_FACTORY_TYPE_ENCODER);
1507       }
1508
1509       if (element) {
1510         if (GST_IS_PRESET (element) &&
1511             gst_preset_load_preset (GST_PRESET (element), profpreset)) {
1512           gchar *settings = _serialize_properties (G_OBJECT (element), NULL);
1513           append_escaped (str,
1514               g_markup_printf_escaped ("preset-properties='%s' ", settings));
1515           g_free (settings);
1516         }
1517         gst_object_unref (element);
1518       }
1519
1520     }
1521
1522     if (profpresetname)
1523       append_escaped (str, g_markup_printf_escaped ("preset-name='%s' ",
1524               profpresetname));
1525
1526     profformat = gst_encoding_profile_get_format (prof);
1527     if (profformat) {
1528       gchar *format = gst_caps_to_string (profformat);
1529       append_escaped (str, g_markup_printf_escaped ("format='%s' ", format));
1530       g_free (format);
1531       gst_caps_unref (profformat);
1532     }
1533
1534     g_string_append (str, ">\n");
1535
1536     if (GST_IS_ENCODING_CONTAINER_PROFILE (prof)) {
1537       guint i = 0;
1538       const GList *tmp2;
1539       GstEncodingContainerProfile *container_prof;
1540
1541       container_prof = GST_ENCODING_CONTAINER_PROFILE (prof);
1542       for (tmp2 = gst_encoding_container_profile_get_profiles (container_prof);
1543           tmp2; tmp2 = tmp2->next, i++) {
1544         GstEncodingProfile *sprof = (GstEncodingProfile *) tmp2->data;
1545         _save_stream_profiles (self, str, sprof, profname, i);
1546       }
1547     }
1548     append_escaped (str,
1549         g_markup_printf_escaped ("      </encoding-profile>\n"));
1550   }
1551   g_list_free (profiles);
1552 }
1553
1554 static GString *
1555 _save (GESFormatter * formatter, GESTimeline * timeline, GError ** error)
1556 {
1557   GString *str;
1558   GESProject *project;
1559
1560   gchar *projstr = NULL, *version;
1561   gchar *properties = NULL, *metas = NULL;
1562   GESXmlFormatter *self = GES_XML_FORMATTER (formatter);
1563   GESXmlFormatterPrivate *priv;
1564
1565
1566   priv = _GET_PRIV (formatter);
1567
1568   priv->min_version = 1;
1569   project = formatter->project;
1570   str = priv->str = g_string_new (NULL);
1571
1572   properties = _serialize_properties (G_OBJECT (project), NULL);
1573   metas = ges_meta_container_metas_to_string (GES_META_CONTAINER (project));
1574   append_escaped (str,
1575       g_markup_printf_escaped ("  <project properties='%s' metadatas='%s'>\n",
1576           properties, metas));
1577   g_free (properties);
1578   g_free (metas);
1579
1580   g_string_append (str, "    <encoding-profiles>\n");
1581   _save_encoding_profiles (GES_XML_FORMATTER (formatter), str, project);
1582   g_string_append (str, "    </encoding-profiles>\n");
1583
1584   g_string_append (str, "    <ressources>\n");
1585   _save_assets (self, str, project);
1586   g_string_append (str, "    </ressources>\n");
1587
1588   _save_timeline (self, str, timeline);
1589   g_string_append (str, "</project>\n</ges>");
1590
1591   projstr = g_strdup_printf ("<ges version='%i.%i'>\n", API_VERSION,
1592       priv->min_version);
1593   g_string_prepend (str, projstr);
1594   g_free (projstr);
1595
1596   ges_meta_container_set_int (GES_META_CONTAINER (project),
1597       GES_META_FORMAT_VERSION, priv->min_version);
1598
1599   version = g_strdup_printf ("%d.%d", API_VERSION,
1600       GES_XML_FORMATTER (formatter)->priv->min_version);
1601
1602   ges_meta_container_set_string (GES_META_CONTAINER (project),
1603       GES_META_FORMAT_VERSION, version);
1604
1605   g_free (version);
1606
1607   priv->str = NULL;
1608
1609   return str;
1610 }
1611
1612 /***********************************************
1613  *                                             *
1614  *   GObject virtual methods implementation    *
1615  *                                             *
1616  ***********************************************/
1617
1618 static void
1619 _get_property (GObject * object, guint property_id,
1620     GValue * value, GParamSpec * pspec)
1621 {
1622 }
1623
1624 static void
1625 _set_property (GObject * object, guint property_id,
1626     const GValue * value, GParamSpec * pspec)
1627 {
1628 }
1629
1630 static void
1631 ges_xml_formatter_init (GESXmlFormatter * self)
1632 {
1633   GESXmlFormatterPrivate *priv = ges_xml_formatter_get_instance_private (self);
1634
1635   priv->project_opened = FALSE;
1636   priv->element_id = g_hash_table_new (g_direct_hash, g_direct_equal);
1637
1638   self->priv = priv;
1639   self->priv->min_version = 1;
1640 }
1641
1642 static void
1643 _dispose (GObject * object)
1644 {
1645   g_clear_pointer (&GES_XML_FORMATTER (object)->priv->element_id,
1646       g_hash_table_unref);
1647
1648   G_OBJECT_CLASS (parent_class)->dispose (object);
1649 }
1650
1651 static void
1652 ges_xml_formatter_class_init (GESXmlFormatterClass * self_class)
1653 {
1654   GObjectClass *object_class = G_OBJECT_CLASS (self_class);
1655   GESBaseXmlFormatterClass *basexmlformatter_class;
1656
1657   basexmlformatter_class = GES_BASE_XML_FORMATTER_CLASS (self_class);
1658
1659   object_class->get_property = _get_property;
1660   object_class->set_property = _set_property;
1661   object_class->dispose = _dispose;
1662
1663   basexmlformatter_class->content_parser.start_element = _parse_element_start;
1664   basexmlformatter_class->content_parser.end_element = _parse_element_end;
1665   basexmlformatter_class->content_parser.text = NULL;
1666   basexmlformatter_class->content_parser.passthrough = NULL;
1667   basexmlformatter_class->content_parser.error = _error_parsing;
1668
1669   ges_formatter_class_register_metas (GES_FORMATTER_CLASS (self_class),
1670       "ges", "GStreamer Editing Services project files",
1671       "xges", "application/ges", VERSION, GST_RANK_PRIMARY);
1672
1673   basexmlformatter_class->save = _save;
1674 }
1675
1676 #undef COLLECT_STR_OPT