Plug some GError leaks when loading assets
[platform/upstream/gst-editing-services.git] / ges / ges-base-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 #endif
23
24 #include "ges.h"
25 #include "ges-internal.h"
26
27 GST_DEBUG_CATEGORY_STATIC (base_xml_formatter);
28 #undef GST_CAT_DEFAULT
29 #define GST_CAT_DEFAULT base_xml_formatter
30
31 #define parent_class ges_base_xml_formatter_parent_class
32
33 #define _GET_PRIV(o)\
34   (((GESBaseXmlFormatter*) o)->priv)
35
36
37 static gboolean _loading_done_cb (GESFormatter * self);
38
39 typedef struct PendingEffects
40 {
41   gchar *track_id;
42   GESTrackElement *trackelement;
43   GstStructure *children_properties;
44   GstStructure *properties;
45
46 } PendingEffects;
47
48 typedef struct PendingBinding
49 {
50   gchar *track_id;
51   GstControlSource *source;
52   gchar *propname;
53   gchar *binding_type;
54 } PendingBinding;
55
56 typedef struct PendingChildProperties
57 {
58   gchar *track_id;
59   GstStructure *structure;
60 } PendingChildProperties;
61
62 typedef struct PendingGroup
63 {
64   GESGroup *group;
65
66   GList *pending_children;
67 } PendingGroup;
68
69 typedef struct PendingClip
70 {
71   gchar *id;
72   guint layer_prio;
73   GstClockTime start;
74   GstClockTime inpoint;
75   GESAsset *asset;
76   GstClockTime duration;
77   GESTrackType track_types;
78   GESLayer *layer;
79
80   GstStructure *properties;
81   GstStructure *children_properties;
82   gchar *metadatas;
83
84   GList *effects;
85
86   GList *pending_bindings;
87
88   GList *children_props;
89
90   /* TODO Implement asset effect management
91    * PendingTrackElements *track_elements; */
92 } PendingClip;
93
94 typedef struct LayerEntry
95 {
96   GESLayer *layer;
97   gboolean auto_trans;
98 } LayerEntry;
99
100 typedef struct PendingAsset
101 {
102   GESFormatter *formatter;
103   gchar *metadatas;
104   GstStructure *properties;
105   gchar *proxy_id;
106 } PendingAsset;
107
108 struct _GESBaseXmlFormatterPrivate
109 {
110   GMarkupParseContext *parsecontext;
111   gboolean check_only;
112
113   /* Asset.id -> PendingClip */
114   GHashTable *assetid_pendingclips;
115
116   /* Clip.ID -> Pending */
117   GHashTable *clipid_pendings;
118
119   /* Clip.ID -> Clip */
120   GHashTable *containers;
121
122   /* ID -> track */
123   GHashTable *tracks;
124
125   /* layer.prio -> LayerEntry */
126   GHashTable *layers;
127
128   /* List of asset waited to be created */
129   GList *pending_assets;
130
131   /* current track element */
132   GESTrackElement *current_track_element;
133
134   GESClip *current_clip;
135   PendingClip *current_pending_clip;
136
137   gboolean timeline_auto_transition;
138
139   GList *groups;
140 };
141
142 static void
143 _free_pending_clip (GESBaseXmlFormatterPrivate * priv, PendingClip * pend);
144
145 static void
146 _free_layer_entry (LayerEntry * entry)
147 {
148   gst_object_unref (entry->layer);
149   g_slice_free (LayerEntry, entry);
150 }
151
152 static void
153 _free_pending_group (PendingGroup * pgroup)
154 {
155   if (pgroup->group)
156     g_object_unref (pgroup->group);
157   g_list_free_full (pgroup->pending_children, g_free);
158   g_slice_free (PendingGroup, pgroup);
159 }
160
161 /*
162 enum
163 {
164   PROP_0,
165   PROP_LAST
166 };
167 static GParamSpec *properties[PROP_LAST];
168
169 enum
170 {
171   LAST_SIGNAL
172 };
173 static guint signals[LAST_SIGNAL];
174 */
175
176 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GESBaseXmlFormatter,
177     ges_base_xml_formatter, GES_TYPE_FORMATTER);
178
179 static GMarkupParseContext *
180 create_parser_context (GESBaseXmlFormatter * self, const gchar * uri,
181     GError ** error)
182 {
183   gsize xmlsize;
184   GFile *file = NULL;
185   gchar *xmlcontent = NULL;
186   GMarkupParseContext *parsecontext = NULL;
187   GESBaseXmlFormatterClass *self_class =
188       GES_BASE_XML_FORMATTER_GET_CLASS (self);
189
190   GError *err = NULL;
191
192   GST_DEBUG_OBJECT (self, "loading xml from %s", uri);
193
194   file = g_file_new_for_uri (uri);
195
196   /* TODO Handle GCancellable */
197   if (!g_file_query_exists (file, NULL)) {
198     err = g_error_new (GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
199         "Invalid URI: \"%s\"", uri);
200     goto failed;
201   }
202
203   if (!g_file_load_contents (file, NULL, &xmlcontent, &xmlsize, NULL, &err))
204     goto failed;
205
206   if (g_strcmp0 (xmlcontent, "") == 0)
207     goto failed;
208
209   parsecontext = g_markup_parse_context_new (&self_class->content_parser,
210       G_MARKUP_TREAT_CDATA_AS_TEXT, self, NULL);
211
212   if (g_markup_parse_context_parse (parsecontext, xmlcontent, xmlsize,
213           &err) == FALSE)
214     goto failed;
215
216   if (!g_markup_parse_context_end_parse (parsecontext, &err))
217     goto failed;
218
219
220 done:
221   g_free (xmlcontent);
222   g_object_unref (file);
223
224   return parsecontext;
225
226 failed:
227   GST_WARNING ("failed to load contents from \"%s\"", uri);
228   g_propagate_error (error, err);
229
230   if (parsecontext) {
231     g_markup_parse_context_free (parsecontext);
232     parsecontext = NULL;
233   }
234
235   goto done;
236 }
237
238 /***********************************************
239  *                                             *
240  * GESFormatter virtual methods implementation *
241  *                                             *
242  ***********************************************/
243
244 static gboolean
245 _can_load_uri (GESFormatter * dummy_formatter, const gchar * uri,
246     GError ** error)
247 {
248   GMarkupParseContext *ctx;
249   GESBaseXmlFormatter *self = GES_BASE_XML_FORMATTER (dummy_formatter);
250
251   /* we create a temporary object so we can use it as a context */
252   _GET_PRIV (self)->check_only = TRUE;
253
254
255   ctx = create_parser_context (self, uri, error);
256   if (!ctx)
257     return FALSE;
258
259   g_markup_parse_context_free (ctx);
260   return TRUE;
261 }
262
263 static gboolean
264 _load_from_uri (GESFormatter * self, GESTimeline * timeline, const gchar * uri,
265     GError ** error)
266 {
267   GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
268
269   ges_timeline_set_auto_transition (timeline, FALSE);
270
271   priv->parsecontext =
272       create_parser_context (GES_BASE_XML_FORMATTER (self), uri, error);
273
274   if (!priv->parsecontext)
275     return FALSE;
276
277   if (g_hash_table_size (priv->assetid_pendingclips) == 0 &&
278       priv->pending_assets == NULL)
279     g_idle_add ((GSourceFunc) _loading_done_cb, g_object_ref (self));
280
281   return TRUE;
282 }
283
284 static gboolean
285 _save_to_uri (GESFormatter * formatter, GESTimeline * timeline,
286     const gchar * uri, gboolean overwrite, GError ** error)
287 {
288   GFile *file;
289   gboolean ret;
290   GString *str;
291   GOutputStream *stream;
292   GError *lerror = NULL;
293
294   g_return_val_if_fail (formatter->project, FALSE);
295
296   file = g_file_new_for_uri (uri);
297   stream = G_OUTPUT_STREAM (g_file_create (file, G_FILE_CREATE_NONE, NULL,
298           &lerror));
299   if (stream == NULL) {
300     if (overwrite && lerror->code == G_IO_ERROR_EXISTS) {
301       g_clear_error (&lerror);
302       stream = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE,
303               G_FILE_CREATE_NONE, NULL, &lerror));
304     }
305
306     if (stream == NULL)
307       goto failed_opening_file;
308   }
309
310   str = GES_BASE_XML_FORMATTER_GET_CLASS (formatter)->save (formatter,
311       timeline, error);
312
313   if (str == NULL)
314     goto serialization_failed;
315
316   ret = g_output_stream_write_all (stream, str->str, str->len, NULL,
317       NULL, &lerror);
318   ret = g_output_stream_close (stream, NULL, &lerror);
319
320   if (ret == FALSE)
321     GST_WARNING_OBJECT (formatter, "Could not save %s because: %s", uri,
322         lerror->message);
323
324   g_string_free (str, TRUE);
325   gst_object_unref (file);
326   gst_object_unref (stream);
327
328   if (lerror)
329     g_propagate_error (error, lerror);
330
331   return ret;
332
333 serialization_failed:
334   gst_object_unref (file);
335
336   g_output_stream_close (stream, NULL, NULL);
337   gst_object_unref (stream);
338   if (lerror)
339     g_propagate_error (error, lerror);
340
341   return FALSE;
342
343 failed_opening_file:
344   gst_object_unref (file);
345
346   GST_WARNING_OBJECT (formatter, "Could not open %s because: %s", uri,
347       lerror->message);
348
349   if (lerror)
350     g_propagate_error (error, lerror);
351
352   return FALSE;
353 }
354
355 /***********************************************
356  *                                             *
357  *   GOBject virtual methods implementation    *
358  *                                             *
359  ***********************************************/
360
361 static void
362 _dispose (GObject * object)
363 {
364   GESBaseXmlFormatterPrivate *priv = _GET_PRIV (object);
365   GList *pendings, *pending_clips_lists;
366
367   pending_clips_lists = g_hash_table_get_values (priv->assetid_pendingclips);
368   for (pendings = pending_clips_lists; pendings; pendings = pendings->next)
369     g_list_free_full (pendings, (GDestroyNotify) _free_pending_clip);
370   g_list_free (pending_clips_lists);
371
372   g_clear_pointer (&priv->assetid_pendingclips, g_hash_table_unref);
373   g_clear_pointer (&priv->containers, g_hash_table_unref);
374   g_clear_pointer (&priv->clipid_pendings, g_hash_table_unref);
375   g_clear_pointer (&priv->tracks, g_hash_table_unref);
376   g_clear_pointer (&priv->layers, g_hash_table_unref);
377
378   G_OBJECT_CLASS (parent_class)->dispose (object);
379 }
380
381 static void
382 _finalize (GObject * object)
383 {
384   GESBaseXmlFormatterPrivate *priv = _GET_PRIV (object);
385
386   if (priv->parsecontext != NULL)
387     g_markup_parse_context_free (priv->parsecontext);
388
389   g_list_free_full (priv->groups, (GDestroyNotify) _free_pending_group);
390   priv->groups = NULL;
391
392   G_OBJECT_CLASS (parent_class)->finalize (object);
393 }
394
395 static void
396 ges_base_xml_formatter_init (GESBaseXmlFormatter * self)
397 {
398   GESBaseXmlFormatterPrivate *priv;
399
400   self->priv = ges_base_xml_formatter_get_instance_private (self);
401
402   priv = self->priv;
403
404   priv->check_only = FALSE;
405   priv->parsecontext = NULL;
406   priv->pending_assets = NULL;
407
408   /* The PendingClip are owned by the assetid_pendingclips table */
409   priv->assetid_pendingclips = g_hash_table_new_full (g_str_hash,
410       g_str_equal, g_free, NULL);
411   priv->clipid_pendings = g_hash_table_new_full (g_str_hash,
412       g_str_equal, g_free, NULL);
413   priv->containers = g_hash_table_new_full (g_str_hash,
414       g_str_equal, g_free, gst_object_unref);
415   priv->tracks = g_hash_table_new_full (g_str_hash,
416       g_str_equal, g_free, gst_object_unref);
417   priv->layers = g_hash_table_new_full (g_direct_hash,
418       g_direct_equal, NULL, (GDestroyNotify) _free_layer_entry);
419   priv->current_track_element = NULL;
420   priv->current_clip = NULL;
421   priv->current_pending_clip = NULL;
422   priv->timeline_auto_transition = FALSE;
423 }
424
425 static void
426 ges_base_xml_formatter_class_init (GESBaseXmlFormatterClass * self_class)
427 {
428   GESFormatterClass *formatter_klass = GES_FORMATTER_CLASS (self_class);
429   GObjectClass *object_class = G_OBJECT_CLASS (self_class);
430
431   object_class->dispose = _dispose;
432   object_class->finalize = _finalize;
433
434   formatter_klass->can_load_uri = _can_load_uri;
435   formatter_klass->load_from_uri = _load_from_uri;
436   formatter_klass->save_to_uri = _save_to_uri;
437
438   self_class->save = NULL;
439
440   GST_DEBUG_CATEGORY_INIT (base_xml_formatter, "base-xml-formatter",
441       GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "Base XML Formatter");
442 }
443
444 /***********************************************
445  *                                             *
446  *             Private methods                 *
447  *                                             *
448  ***********************************************/
449
450
451 static GESTrackElement *
452 _get_element_by_track_id (GESBaseXmlFormatterPrivate * priv,
453     const gchar * track_id, GESClip * clip)
454 {
455   GESTrack *track = g_hash_table_lookup (priv->tracks, track_id);
456
457   return ges_clip_find_track_element (clip, track, GES_TYPE_SOURCE);
458 }
459
460 static void
461 _set_auto_transition (gpointer prio, LayerEntry * entry, gpointer udata)
462 {
463   ges_layer_set_auto_transition (entry->layer, entry->auto_trans);
464 }
465
466 static void
467 _add_all_groups (GESFormatter * self)
468 {
469   GList *tmp;
470   GESTimelineElement *child;
471   GESBaseXmlFormatterPrivate *priv = GES_BASE_XML_FORMATTER (self)->priv;
472
473   for (tmp = priv->groups; tmp; tmp = tmp->next) {
474     GList *lchild;
475     PendingGroup *pgroup = tmp->data;
476
477     timeline_add_group (self->timeline, pgroup->group);
478
479     for (lchild = ((PendingGroup *) tmp->data)->pending_children; lchild;
480         lchild = lchild->next) {
481       child = g_hash_table_lookup (priv->containers, lchild->data);
482
483       GST_DEBUG_OBJECT (tmp->data, "Adding %s child %" GST_PTR_FORMAT " %s",
484           (const gchar *) lchild->data, child,
485           GES_TIMELINE_ELEMENT_NAME (child));
486       ges_container_add (GES_CONTAINER (pgroup->group), child);
487     }
488     pgroup->group = NULL;
489   }
490
491   g_list_free_full (priv->groups, (GDestroyNotify) _free_pending_group);
492   priv->groups = NULL;
493 }
494
495 static void
496 _loading_done (GESFormatter * self)
497 {
498   GList *assets, *tmp;
499   GESBaseXmlFormatterPrivate *priv = GES_BASE_XML_FORMATTER (self)->priv;
500
501   _add_all_groups (self);
502
503   if (priv->parsecontext)
504     g_markup_parse_context_free (priv->parsecontext);
505   priv->parsecontext = NULL;
506
507   ges_timeline_set_auto_transition (self->timeline,
508       priv->timeline_auto_transition);
509
510   /* Go over all assets and make sure that all proxies we were 'trying' to set are finally
511    * properly set */
512   assets = ges_project_list_assets (self->project, GES_TYPE_EXTRACTABLE);
513   for (tmp = assets; tmp; tmp = tmp->next) {
514     ges_asset_set_proxy (NULL, tmp->data);
515   }
516   g_list_free (assets);
517
518   g_hash_table_foreach (priv->layers, (GHFunc) _set_auto_transition, NULL);
519   ges_project_set_loaded (self->project, self);
520 }
521
522 static gboolean
523 _loading_done_cb (GESFormatter * self)
524 {
525   _loading_done (self);
526   gst_object_unref (self);
527
528   return FALSE;
529 }
530
531 static gboolean
532 _set_child_property (GQuark field_id, const GValue * value,
533     GESTimelineElement * tlelement)
534 {
535   GParamSpec *pspec;
536   GObject *object;
537
538   /* FIXME: error handling? */
539   if (!ges_timeline_element_lookup_child (tlelement,
540           g_quark_to_string (field_id), &object, &pspec)) {
541 #ifndef GST_DISABLE_GST_DEBUG
542     gchar *tmp = gst_value_serialize (value);
543     GST_ERROR_OBJECT (tlelement, "Could not set %s=%s",
544         g_quark_to_string (field_id), tmp);
545     g_free (tmp);
546 #endif
547     return TRUE;
548   }
549
550   g_object_set_property (G_OBJECT (object), pspec->name, value);
551   g_param_spec_unref (pspec);
552   gst_object_unref (object);
553   return TRUE;
554 }
555
556 gboolean
557 set_property_foreach (GQuark field_id, const GValue * value, GObject * object)
558 {
559   g_object_set_property (object, g_quark_to_string (field_id), value);
560   return TRUE;
561 }
562
563 static inline GESClip *
564 _add_object_to_layer (GESBaseXmlFormatterPrivate * priv, const gchar * id,
565     GESLayer * layer, GESAsset * asset, GstClockTime start,
566     GstClockTime inpoint, GstClockTime duration,
567     GESTrackType track_types, const gchar * metadatas,
568     GstStructure * properties, GstStructure * children_properties)
569 {
570   GESClip *clip = ges_layer_add_asset (layer,
571       asset, start, inpoint, duration, track_types);
572
573   if (clip == NULL) {
574     GST_WARNING_OBJECT (clip, "Could not add object from asset: %s",
575         ges_asset_get_id (asset));
576
577     return NULL;
578   }
579
580   if (metadatas)
581     ges_meta_container_add_metas_from_string (GES_META_CONTAINER (clip),
582         metadatas);
583
584   if (properties)
585     gst_structure_foreach (properties,
586         (GstStructureForeachFunc) set_property_foreach, clip);
587
588   if (children_properties)
589     gst_structure_foreach (children_properties,
590         (GstStructureForeachFunc) _set_child_property, clip);
591
592   g_hash_table_insert (priv->containers, g_strdup (id), gst_object_ref (clip));
593   return clip;
594 }
595
596 static void
597 _add_track_element (GESFormatter * self, GESClip * clip,
598     GESTrackElement * trackelement, const gchar * track_id,
599     GstStructure * children_properties, GstStructure * properties)
600 {
601   GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
602   GESTrack *track = g_hash_table_lookup (priv->tracks, track_id);
603
604   if (track == NULL) {
605     GST_WARNING_OBJECT (self, "No track with id %s, can not add trackelement",
606         track_id);
607     gst_object_unref (trackelement);
608     return;
609   }
610
611   GST_DEBUG_OBJECT (self, "Adding track_element: %" GST_PTR_FORMAT
612       " To : %" GST_PTR_FORMAT, trackelement, clip);
613
614   ges_container_add (GES_CONTAINER (clip), GES_TIMELINE_ELEMENT (trackelement));
615   gst_structure_foreach (children_properties,
616       (GstStructureForeachFunc) _set_child_property, trackelement);
617
618   if (properties) {
619     /* We do not serialize the priority anymore, and we should never have. */
620     gst_structure_remove_field (properties, "priority");
621     gst_structure_foreach (properties,
622         (GstStructureForeachFunc) set_property_foreach, trackelement);
623   }
624 }
625
626 static void
627 _free_pending_children_props (PendingChildProperties * pend)
628 {
629   g_free (pend->track_id);
630   if (pend->structure)
631     gst_structure_free (pend->structure);
632 }
633
634 static void
635 _free_pending_binding (PendingBinding * pend)
636 {
637   g_free (pend->propname);
638   g_free (pend->binding_type);
639   g_free (pend->track_id);
640 }
641
642 static void
643 _free_pending_effect (PendingEffects * pend)
644 {
645   g_free (pend->track_id);
646   gst_object_unref (pend->trackelement);
647   if (pend->children_properties)
648     gst_structure_free (pend->children_properties);
649   if (pend->properties)
650     gst_structure_free (pend->properties);
651
652   g_slice_free (PendingEffects, pend);
653 }
654
655 static void
656 _free_pending_clip (GESBaseXmlFormatterPrivate * priv, PendingClip * pend)
657 {
658   gst_object_unref (pend->layer);
659   if (pend->properties)
660     gst_structure_free (pend->properties);
661   g_list_free_full (pend->effects, (GDestroyNotify) _free_pending_effect);
662   g_list_free_full (pend->pending_bindings,
663       (GDestroyNotify) _free_pending_binding);
664   g_list_free_full (pend->children_props,
665       (GDestroyNotify) _free_pending_children_props);
666   g_hash_table_remove (priv->clipid_pendings, pend->id);
667   g_free (pend->id);
668   g_slice_free (PendingClip, pend);
669 }
670
671 static void
672 _free_pending_asset (GESBaseXmlFormatterPrivate * priv, PendingAsset * passet)
673 {
674   g_free (passet->metadatas);
675   g_free (passet->proxy_id);
676   if (passet->properties)
677     gst_structure_free (passet->properties);
678
679   priv->pending_assets = g_list_remove (priv->pending_assets, passet);
680   g_slice_free (PendingAsset, passet);
681 }
682
683 static void
684 _add_children_properties (GESBaseXmlFormatterPrivate * priv, GList * childprops,
685     GESClip * clip)
686 {
687   GList *tmpchildprops;
688
689   for (tmpchildprops = childprops; tmpchildprops;
690       tmpchildprops = tmpchildprops->next) {
691     PendingChildProperties *pchildprops = tmpchildprops->data;
692     GESTrackElement *element =
693         _get_element_by_track_id (priv, pchildprops->track_id, clip);
694     if (element && pchildprops->structure)
695       gst_structure_foreach (pchildprops->structure,
696           (GstStructureForeachFunc) _set_child_property, element);
697   }
698 }
699
700 static void
701 _add_pending_bindings (GESBaseXmlFormatterPrivate * priv, GList * bindings,
702     GESClip * clip)
703 {
704   GList *tmpbinding;
705
706   for (tmpbinding = bindings; tmpbinding; tmpbinding = tmpbinding->next) {
707     PendingBinding *pbinding = tmpbinding->data;
708     GESTrackElement *element =
709         _get_element_by_track_id (priv, pbinding->track_id, clip);
710     if (element)
711       ges_track_element_set_control_source (element,
712           pbinding->source, pbinding->propname, pbinding->binding_type);
713   }
714 }
715
716 static void
717 new_asset_cb (GESAsset * source, GAsyncResult * res, PendingAsset * passet)
718 {
719   GError *error = NULL;
720   gchar *possible_id = NULL;
721   GList *tmp, *pendings = NULL;
722   GESFormatter *self = passet->formatter;
723   const gchar *id = ges_asset_get_id (source);
724   GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
725   GESAsset *asset = ges_asset_request_finish (res, &error);
726
727   if (error) {
728     GST_LOG_OBJECT (self, "Error %s creating asset id: %s", error->message, id);
729
730     /* We set the metas on the Asset to give hints to the user */
731     if (passet->metadatas)
732       ges_meta_container_add_metas_from_string (GES_META_CONTAINER (source),
733           passet->metadatas);
734     if (passet->properties)
735       gst_structure_foreach (passet->properties,
736           (GstStructureForeachFunc) set_property_foreach, source);
737
738     possible_id = ges_project_try_updating_id (GES_FORMATTER (self)->project,
739         source, error);
740
741     if (possible_id == NULL) {
742       GST_WARNING_OBJECT (self, "Abandoning creation of asset %s with ID %s"
743           "- Error: %s", g_type_name (G_OBJECT_TYPE (source)), id,
744           error->message);
745
746       pendings = g_hash_table_lookup (priv->assetid_pendingclips, id);
747       _free_pending_asset (priv, passet);
748       goto done;
749     }
750
751     /* We got a possible ID replacement for that asset, create it, and
752      * make sure the assetid_pendingclips will use it */
753     ges_asset_request_async (ges_asset_get_extractable_type (source),
754         possible_id, NULL, (GAsyncReadyCallback) new_asset_cb, passet);
755     ges_project_add_loading_asset (GES_FORMATTER (self)->project,
756         ges_asset_get_extractable_type (source), possible_id);
757
758     pendings = g_hash_table_lookup (priv->assetid_pendingclips, id);
759     if (pendings) {
760       g_hash_table_remove (priv->assetid_pendingclips, id);
761       g_hash_table_insert (priv->assetid_pendingclips,
762           g_strdup (possible_id), pendings);
763
764       /* pendings should no be freed */
765       pendings = NULL;
766     }
767     goto done;
768   }
769
770   if (passet->proxy_id) {
771     /* We set the URI to be used as a proxy,
772      * this will finally be set as the proxy when we
773      * are done loading all assets */
774     ges_asset_try_proxy (asset, passet->proxy_id);
775   }
776
777   /* now that we have the GESAsset, we create the GESClips */
778   pendings = g_hash_table_lookup (priv->assetid_pendingclips, id);
779   GST_DEBUG_OBJECT (self, "Asset created with ID %s, now creating pending "
780       " Clips, nb pendings: %i", id, g_list_length (pendings));
781   for (tmp = pendings; tmp; tmp = tmp->next) {
782     GList *tmpeffect;
783     GESClip *clip;
784     PendingClip *pend = (PendingClip *) tmp->data;
785
786     clip =
787         _add_object_to_layer (priv, pend->id, pend->layer, asset,
788         pend->start, pend->inpoint, pend->duration, pend->track_types,
789         pend->metadatas, pend->properties, pend->children_properties);
790
791     if (clip == NULL)
792       continue;
793
794     _add_children_properties (priv, pend->children_props, clip);
795     _add_pending_bindings (priv, pend->pending_bindings, clip);
796
797     GST_DEBUG_OBJECT (self, "Adding %i effect to new object",
798         g_list_length (pend->effects));
799     for (tmpeffect = pend->effects; tmpeffect; tmpeffect = tmpeffect->next) {
800       PendingEffects *peffect = (PendingEffects *) tmpeffect->data;
801
802       /* We keep a ref as _free_pending_effect unrefs it */
803       _add_track_element (self, clip, gst_object_ref (peffect->trackelement),
804           peffect->track_id, peffect->children_properties, peffect->properties);
805     }
806   }
807
808   /* And now add to the project */
809   ges_project_add_asset (self->project, asset);
810   gst_object_unref (self);
811
812   _free_pending_asset (priv, passet);
813
814 done:
815   if (asset)
816     gst_object_unref (asset);
817   if (possible_id)
818     g_free (possible_id);
819
820   g_clear_error (&error);
821
822   if (pendings) {
823     for (tmp = pendings; tmp; tmp = tmp->next)
824       _free_pending_clip (priv, tmp->data);
825     g_hash_table_remove (priv->assetid_pendingclips, id);
826     g_list_free (pendings);
827   }
828
829   if (g_hash_table_size (priv->assetid_pendingclips) == 0 &&
830       priv->pending_assets == NULL)
831     _loading_done (self);
832 }
833
834 GstElement *
835 get_element_for_encoding_profile (GstEncodingProfile * prof,
836     GstElementFactoryListType type)
837 {
838   GstEncodingProfile *prof_copy;
839   GstElement *encodebin;
840   GList *tmp;
841   GstElement *element = NULL;
842
843   prof_copy = gst_encoding_profile_copy (prof);
844
845   gst_encoding_profile_set_presence (prof_copy, 1);
846   gst_encoding_profile_set_preset (prof_copy, NULL);
847
848   encodebin = gst_element_factory_make ("encodebin", NULL);
849   g_object_set (encodebin, "profile", prof_copy, NULL);
850
851   GST_OBJECT_LOCK (encodebin);
852   for (tmp = GST_BIN (encodebin)->children; tmp; tmp = tmp->next) {
853     GstElementFactory *factory;
854     factory = gst_element_get_factory (GST_ELEMENT (tmp->data));
855
856     if (factory && gst_element_factory_list_is_type (factory, type)) {
857       element = GST_ELEMENT (tmp->data);
858       gst_object_ref (element);
859       break;
860     }
861   }
862   GST_OBJECT_UNLOCK (encodebin);
863   gst_object_unref (encodebin);
864
865   gst_encoding_profile_unref (prof_copy);
866
867   return element;
868 }
869
870 static GstEncodingProfile *
871 _create_profile (GESBaseXmlFormatter * self,
872     const gchar * type, const gchar * parent, const gchar * name,
873     const gchar * description, GstCaps * format, const gchar * preset,
874     GstStructure * preset_properties, const gchar * preset_name, gint id,
875     guint presence, GstCaps * restriction, guint pass,
876     gboolean variableframerate, gboolean enabled)
877 {
878   GstEncodingProfile *profile = NULL;
879
880   if (!g_strcmp0 (type, "container")) {
881     profile = GST_ENCODING_PROFILE (gst_encoding_container_profile_new (name,
882             description, format, preset));
883     gst_encoding_profile_set_preset_name (profile, preset_name);
884   } else if (!g_strcmp0 (type, "video")) {
885     GstEncodingVideoProfile *sprof = gst_encoding_video_profile_new (format,
886         preset, restriction, presence);
887
888     gst_encoding_video_profile_set_variableframerate (sprof, variableframerate);
889     gst_encoding_video_profile_set_pass (sprof, pass);
890
891     profile = GST_ENCODING_PROFILE (sprof);
892   } else if (!g_strcmp0 (type, "audio")) {
893     profile = GST_ENCODING_PROFILE (gst_encoding_audio_profile_new (format,
894             preset, restriction, presence));
895   } else {
896     GST_ERROR_OBJECT (self, "Unknown profile format '%s'", type);
897
898     return NULL;
899   }
900
901   if (!g_strcmp0 (type, "video") || !g_strcmp0 (type, "audio")) {
902     gst_encoding_profile_set_name (profile, name);
903     gst_encoding_profile_set_enabled (profile, enabled);
904     gst_encoding_profile_set_description (profile, description);
905     gst_encoding_profile_set_preset_name (profile, preset_name);
906   }
907
908   if (preset && preset_properties) {
909     GstElement *element;
910
911     if (!g_strcmp0 (type, "container")) {
912       element = get_element_for_encoding_profile (profile,
913           GST_ELEMENT_FACTORY_TYPE_MUXER);
914     } else {
915       element = get_element_for_encoding_profile (profile,
916           GST_ELEMENT_FACTORY_TYPE_ENCODER);
917     }
918
919     if (G_UNLIKELY (!element || !GST_IS_PRESET (element))) {
920       GST_WARNING_OBJECT (element, "Element is not a GstPreset");
921       goto done;
922     }
923
924     /* If the preset doesn't exist on the system, create it */
925     if (!gst_preset_load_preset (GST_PRESET (element), preset)) {
926       gst_structure_foreach (preset_properties,
927           (GstStructureForeachFunc) set_property_foreach, element);
928
929       if (!gst_preset_save_preset (GST_PRESET (element), preset)) {
930         GST_WARNING_OBJECT (element, "Could not save preset %s", preset);
931       }
932     }
933
934   done:
935     if (element)
936       gst_object_unref (element);
937   }
938
939   return profile;
940 }
941
942 /***********************************************
943  *                                             *
944  *              Public methods                 *
945  *                                             *
946  ***********************************************/
947
948 void
949 ges_base_xml_formatter_add_asset (GESBaseXmlFormatter * self,
950     const gchar * id, GType extractable_type, GstStructure * properties,
951     const gchar * metadatas, const gchar * proxy_id, GError ** error)
952 {
953   PendingAsset *passet;
954   GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
955
956   if (priv->check_only)
957     return;
958
959   passet = g_slice_new0 (PendingAsset);
960   passet->metadatas = g_strdup (metadatas);
961   passet->proxy_id = g_strdup (proxy_id);
962   passet->formatter = gst_object_ref (self);
963   if (properties)
964     passet->properties = gst_structure_copy (properties);
965
966   ges_asset_request_async (extractable_type, id, NULL,
967       (GAsyncReadyCallback) new_asset_cb, passet);
968   ges_project_add_loading_asset (GES_FORMATTER (self)->project,
969       extractable_type, id);
970   priv->pending_assets = g_list_prepend (priv->pending_assets, passet);
971 }
972
973 void
974 ges_base_xml_formatter_add_clip (GESBaseXmlFormatter * self,
975     const gchar * id, const char *asset_id, GType type, GstClockTime start,
976     GstClockTime inpoint, GstClockTime duration,
977     guint layer_prio, GESTrackType track_types, GstStructure * properties,
978     GstStructure * children_properties,
979     const gchar * metadatas, GError ** error)
980 {
981   GESAsset *asset;
982   GESClip *nclip;
983   LayerEntry *entry;
984   GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
985
986   if (priv->check_only)
987     return;
988
989   entry = g_hash_table_lookup (priv->layers, GINT_TO_POINTER (layer_prio));
990   if (entry == NULL) {
991     g_set_error (error, GES_ERROR, GES_ERROR_FORMATTER_MALFORMED_INPUT_FILE,
992         "We got a Clip in a layer"
993         " that does not exist, something is wrong either in the project file or"
994         " in %s", g_type_name (G_OBJECT_TYPE (self)));
995     return;
996   }
997
998   /* We do not want the properties that are passed to layer-add_asset to be reset */
999   if (properties)
1000     gst_structure_remove_fields (properties, "supported-formats",
1001         "inpoint", "start", "duration", NULL);
1002
1003   asset = ges_asset_request (type, asset_id, NULL);
1004   if (asset == NULL) {
1005     gchar *real_id;
1006     PendingClip *pclip;
1007     GList *pendings;
1008
1009     real_id = ges_extractable_type_check_id (type, asset_id, error);
1010     if (real_id == NULL) {
1011       if (*error == NULL)
1012         g_set_error (error, G_MARKUP_ERROR,
1013             G_MARKUP_ERROR_INVALID_CONTENT,
1014             "Object type '%s' with Asset id: %s not be created'",
1015             g_type_name (type), asset_id);
1016
1017       return;
1018     }
1019
1020     pendings = g_hash_table_lookup (priv->assetid_pendingclips, asset_id);
1021
1022     pclip = g_slice_new0 (PendingClip);
1023     GST_DEBUG_OBJECT (self, "Adding pending %p for %s, currently: %i",
1024         pclip, asset_id, g_list_length (pendings));
1025
1026     pclip->id = g_strdup (id);
1027     pclip->track_types = track_types;
1028     pclip->duration = duration;
1029     pclip->inpoint = inpoint;
1030     pclip->start = start;
1031     pclip->layer = gst_object_ref (entry->layer);
1032
1033     pclip->properties = properties ? gst_structure_copy (properties) : NULL;
1034     pclip->children_properties =
1035         children_properties ? gst_structure_copy (children_properties) : NULL;
1036     pclip->metadatas = g_strdup (metadatas);
1037
1038     /* Add the new pending object to the hashtable */
1039     g_hash_table_insert (priv->assetid_pendingclips, real_id,
1040         g_list_append (pendings, pclip));
1041     g_hash_table_insert (priv->clipid_pendings, g_strdup (id), pclip);
1042
1043     priv->current_clip = NULL;
1044     priv->current_pending_clip = pclip;
1045
1046     return;
1047   }
1048
1049   nclip = _add_object_to_layer (priv, id, entry->layer,
1050       asset, start, inpoint, duration, track_types, metadatas, properties,
1051       children_properties);
1052
1053   if (!nclip)
1054     return;
1055
1056   priv->current_clip = nclip;
1057 }
1058
1059 void
1060 ges_base_xml_formatter_set_timeline_properties (GESBaseXmlFormatter * self,
1061     GESTimeline * timeline, const gchar * properties, const gchar * metadatas)
1062 {
1063   GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
1064   gboolean auto_transition = FALSE;
1065
1066   if (properties) {
1067     GstStructure *props = gst_structure_from_string (properties, NULL);
1068
1069     if (props) {
1070       if (gst_structure_get_boolean (props, "auto-transition",
1071               &auto_transition))
1072         gst_structure_remove_field (props, "auto-transition");
1073
1074       gst_structure_foreach (props,
1075           (GstStructureForeachFunc) set_property_foreach, timeline);
1076       gst_structure_free (props);
1077     }
1078   }
1079
1080   if (metadatas) {
1081     ges_meta_container_add_metas_from_string (GES_META_CONTAINER (timeline),
1082         metadatas);
1083   };
1084
1085   priv->timeline_auto_transition = auto_transition;
1086 }
1087
1088 void
1089 ges_base_xml_formatter_add_layer (GESBaseXmlFormatter * self,
1090     GType extractable_type, guint priority, GstStructure * properties,
1091     const gchar * metadatas, GError ** error)
1092 {
1093   LayerEntry *entry;
1094   GESAsset *asset;
1095   GESLayer *layer;
1096   gboolean auto_transition = FALSE;
1097   GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
1098
1099   if (priv->check_only)
1100     return;
1101
1102   if (extractable_type == G_TYPE_NONE)
1103     layer = ges_layer_new ();
1104   else {
1105     asset = ges_asset_request (extractable_type, NULL, error);
1106     if (asset == NULL) {
1107       if (error && *error == NULL) {
1108         g_set_error (error, G_MARKUP_ERROR,
1109             G_MARKUP_ERROR_INVALID_CONTENT,
1110             "Layer type %s could not be created'",
1111             g_type_name (extractable_type));
1112         return;
1113       }
1114     }
1115     layer = GES_LAYER (ges_asset_extract (asset, error));
1116   }
1117
1118   ges_layer_set_priority (layer, priority);
1119   ges_timeline_add_layer (GES_FORMATTER (self)->timeline, layer);
1120   if (properties) {
1121     if (gst_structure_get_boolean (properties, "auto-transition",
1122             &auto_transition))
1123       gst_structure_remove_field (properties, "auto-transition");
1124
1125     gst_structure_foreach (properties,
1126         (GstStructureForeachFunc) set_property_foreach, layer);
1127   }
1128
1129   if (metadatas)
1130     ges_meta_container_add_metas_from_string (GES_META_CONTAINER (layer),
1131         metadatas);
1132
1133   entry = g_slice_new0 (LayerEntry);
1134   entry->layer = gst_object_ref (layer);
1135   entry->auto_trans = auto_transition;
1136
1137   g_hash_table_insert (priv->layers, GINT_TO_POINTER (priority), entry);
1138 }
1139
1140 void
1141 ges_base_xml_formatter_add_track (GESBaseXmlFormatter * self,
1142     GESTrackType track_type, GstCaps * caps, const gchar * id,
1143     GstStructure * properties, const gchar * metadatas, GError ** error)
1144 {
1145   GESTrack *track;
1146   GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
1147
1148   if (priv->check_only) {
1149     return;
1150   }
1151
1152   track = ges_track_new (track_type, caps);
1153   ges_timeline_add_track (GES_FORMATTER (self)->timeline, track);
1154
1155   if (properties) {
1156     gchar *restriction;
1157     GstCaps *restriction_caps;
1158
1159     gst_structure_get (properties, "restriction-caps", G_TYPE_STRING,
1160         &restriction, NULL);
1161     gst_structure_remove_fields (properties, "restriction-caps", "caps",
1162         "message-forward", NULL);
1163     if (g_strcmp0 (restriction, "NULL")) {
1164       restriction_caps = gst_caps_from_string (restriction);
1165       ges_track_set_restriction_caps (track, restriction_caps);
1166       gst_caps_unref (restriction_caps);
1167     }
1168     gst_structure_foreach (properties,
1169         (GstStructureForeachFunc) set_property_foreach, track);
1170     g_free (restriction);
1171   }
1172
1173   g_hash_table_insert (priv->tracks, g_strdup (id), gst_object_ref (track));
1174   if (metadatas)
1175     ges_meta_container_add_metas_from_string (GES_META_CONTAINER (track),
1176         metadatas);
1177 }
1178
1179 void
1180 ges_base_xml_formatter_add_control_binding (GESBaseXmlFormatter * self,
1181     const gchar * binding_type, const gchar * source_type,
1182     const gchar * property_name, gint mode, const gchar * track_id,
1183     GSList * timed_values)
1184 {
1185   GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
1186   GESTrackElement *element = NULL;
1187
1188   if (track_id[0] != '-' && priv->current_clip)
1189     element = _get_element_by_track_id (priv, track_id, priv->current_clip);
1190
1191   else if (track_id[0] != '-' && priv->current_pending_clip) {
1192     PendingBinding *pbinding;
1193
1194     pbinding = g_slice_new0 (PendingBinding);
1195     pbinding->source = gst_interpolation_control_source_new ();
1196     g_object_set (pbinding->source, "mode", mode, NULL);
1197     gst_timed_value_control_source_set_from_list (GST_TIMED_VALUE_CONTROL_SOURCE
1198         (pbinding->source), timed_values);
1199     pbinding->propname = g_strdup (property_name);
1200     pbinding->binding_type = g_strdup (binding_type);
1201     pbinding->track_id = g_strdup (track_id);
1202     priv->current_pending_clip->pending_bindings =
1203         g_list_append (priv->current_pending_clip->pending_bindings, pbinding);
1204     return;
1205   }
1206
1207   else {
1208     element = priv->current_track_element;
1209   }
1210
1211   if (element == NULL) {
1212     GST_WARNING ("No current track element to which we can append a binding");
1213     return;
1214   }
1215
1216   if (!g_strcmp0 (source_type, "interpolation")) {
1217     GstControlSource *source;
1218
1219     source = gst_interpolation_control_source_new ();
1220     ges_track_element_set_control_source (element, source,
1221         property_name, binding_type);
1222
1223     g_object_set (source, "mode", mode, NULL);
1224
1225     gst_timed_value_control_source_set_from_list (GST_TIMED_VALUE_CONTROL_SOURCE
1226         (source), timed_values);
1227   } else
1228     GST_WARNING ("This interpolation type is not supported\n");
1229 }
1230
1231 void
1232 ges_base_xml_formatter_add_source (GESBaseXmlFormatter * self,
1233     const gchar * track_id, GstStructure * children_properties)
1234 {
1235   GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
1236   GESTrackElement *element = NULL;
1237
1238   if (track_id[0] != '-' && priv->current_clip)
1239     element = _get_element_by_track_id (priv, track_id, priv->current_clip);
1240
1241   else if (track_id[0] != '-' && priv->current_pending_clip) {
1242     PendingChildProperties *pchildprops;
1243
1244     pchildprops = g_slice_new0 (PendingChildProperties);
1245     pchildprops->track_id = g_strdup (track_id);
1246     pchildprops->structure = children_properties ?
1247         gst_structure_copy (children_properties) : NULL;
1248     priv->current_pending_clip->children_props =
1249         g_list_append (priv->current_pending_clip->children_props, pchildprops);
1250     return;
1251   } else {
1252     element = priv->current_track_element;
1253   }
1254
1255   if (element == NULL) {
1256     GST_WARNING
1257         ("No current track element to which we can append children properties");
1258     return;
1259   }
1260
1261   gst_structure_foreach (children_properties,
1262       (GstStructureForeachFunc) _set_child_property, element);
1263 }
1264
1265 void
1266 ges_base_xml_formatter_add_track_element (GESBaseXmlFormatter * self,
1267     GType track_element_type, const gchar * asset_id, const gchar * track_id,
1268     const gchar * timeline_obj_id, GstStructure * children_properties,
1269     GstStructure * properties, const gchar * metadatas, GError ** error)
1270 {
1271   GESTrackElement *trackelement;
1272
1273   GError *err = NULL;
1274   GESAsset *asset = NULL;
1275   GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
1276
1277   if (priv->check_only)
1278     return;
1279
1280   if (g_type_is_a (track_element_type, GES_TYPE_TRACK_ELEMENT) == FALSE) {
1281     GST_DEBUG_OBJECT (self, "%s is not a TrackElement, can not create it",
1282         g_type_name (track_element_type));
1283     goto out;
1284   }
1285
1286   if (g_type_is_a (track_element_type, GES_TYPE_BASE_EFFECT) == FALSE) {
1287     GST_FIXME_OBJECT (self, "%s currently not supported",
1288         g_type_name (track_element_type));
1289     goto out;
1290   }
1291
1292   asset = ges_asset_request (track_element_type, asset_id, &err);
1293   if (asset == NULL) {
1294     GST_DEBUG_OBJECT (self, "Can not create trackelement %s", asset_id);
1295     GST_FIXME_OBJECT (self, "Check if missing plugins etc %s",
1296         err ? err->message : "");
1297
1298     goto out;
1299   }
1300
1301   trackelement = GES_TRACK_ELEMENT (ges_asset_extract (asset, NULL));
1302   if (trackelement) {
1303     GESClip *clip;
1304     if (metadatas)
1305       ges_meta_container_add_metas_from_string (GES_META_CONTAINER
1306           (trackelement), metadatas);
1307
1308     clip = g_hash_table_lookup (priv->containers, timeline_obj_id);
1309     if (clip) {
1310       _add_track_element (GES_FORMATTER (self), clip, trackelement, track_id,
1311           children_properties, properties);
1312     } else {
1313       PendingEffects *peffect;
1314       PendingClip *pend = g_hash_table_lookup (priv->clipid_pendings,
1315           timeline_obj_id);
1316       if (pend == NULL) {
1317         GST_WARNING_OBJECT (self, "No Clip with id: %s can not "
1318             "add TrackElement", timeline_obj_id);
1319         goto out;
1320       }
1321
1322       peffect = g_slice_new0 (PendingEffects);
1323
1324       peffect->trackelement = trackelement;
1325       peffect->track_id = g_strdup (track_id);
1326       peffect->properties = properties ? gst_structure_copy (properties) : NULL;
1327       peffect->children_properties = children_properties ?
1328           gst_structure_copy (children_properties) : NULL;
1329
1330       pend->effects = g_list_append (pend->effects, peffect);
1331     }
1332     priv->current_track_element = trackelement;
1333   }
1334
1335   ges_project_add_asset (GES_FORMATTER (self)->project, asset);
1336
1337 out:
1338   if (asset)
1339     gst_object_unref (asset);
1340   if (err)
1341     g_error_free (err);
1342
1343   return;
1344 }
1345
1346 void
1347 ges_base_xml_formatter_add_encoding_profile (GESBaseXmlFormatter * self,
1348     const gchar * type, const gchar * parent, const gchar * name,
1349     const gchar * description, GstCaps * format, const gchar * preset,
1350     GstStructure * preset_properties, const gchar * preset_name, guint id,
1351     guint presence, GstCaps * restriction, guint pass,
1352     gboolean variableframerate, GstStructure * properties, gboolean enabled,
1353     GError ** error)
1354 {
1355   const GList *tmp;
1356   GstEncodingProfile *profile;
1357   GstEncodingContainerProfile *parent_profile = NULL;
1358   GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
1359
1360   if (priv->check_only)
1361     goto done;
1362
1363   if (parent == NULL) {
1364     profile =
1365         _create_profile (self, type, parent, name, description, format, preset,
1366         preset_properties, preset_name, id, presence, restriction, pass,
1367         variableframerate, enabled);
1368     ges_project_add_encoding_profile (GES_FORMATTER (self)->project, profile);
1369     gst_object_unref (profile);
1370
1371     goto done;
1372   }
1373
1374   for (tmp = ges_project_list_encoding_profiles (GES_FORMATTER (self)->project);
1375       tmp; tmp = tmp->next) {
1376     GstEncodingProfile *tmpprofile = GST_ENCODING_PROFILE (tmp->data);
1377
1378     if (g_strcmp0 (gst_encoding_profile_get_name (tmpprofile),
1379             gst_encoding_profile_get_name (tmpprofile)) == 0) {
1380
1381       if (!GST_IS_ENCODING_CONTAINER_PROFILE (tmpprofile)) {
1382         g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
1383             "Profile '%s' parent %s is not a container...'", name, parent);
1384         goto done;
1385       }
1386
1387       parent_profile = GST_ENCODING_CONTAINER_PROFILE (tmpprofile);
1388       break;
1389     }
1390   }
1391
1392   if (parent_profile == NULL) {
1393     g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
1394         "Profile '%s' parent %s does not exist'", name, parent);
1395     goto done;
1396   }
1397
1398   profile =
1399       _create_profile (self, type, parent, name, description, format, preset,
1400       preset_properties, preset_name, id, presence, restriction, pass,
1401       variableframerate, enabled);
1402
1403   if (profile == NULL)
1404     goto done;
1405
1406   gst_encoding_container_profile_add_profile (parent_profile, profile);
1407
1408 done:
1409   if (format)
1410     gst_caps_unref (format);
1411   if (restriction)
1412     gst_caps_unref (restriction);
1413 }
1414
1415 void
1416 ges_base_xml_formatter_add_group (GESBaseXmlFormatter * self,
1417     const gchar * id, const gchar * properties, const gchar * metadatas)
1418 {
1419   PendingGroup *pgroup;
1420   GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
1421
1422   if (priv->check_only)
1423     return;
1424
1425   pgroup = g_slice_new0 (PendingGroup);
1426   pgroup->group = ges_group_new ();
1427
1428   if (metadatas)
1429     ges_meta_container_add_metas_from_string (GES_META_CONTAINER
1430         (pgroup->group), metadatas);
1431
1432   g_hash_table_insert (priv->containers, g_strdup (id),
1433       gst_object_ref (pgroup->group));
1434   priv->groups = g_list_prepend (priv->groups, pgroup);
1435
1436   return;
1437 }
1438
1439 void
1440 ges_base_xml_formatter_last_group_add_child (GESBaseXmlFormatter * self,
1441     const gchar * child_id, const gchar * name)
1442 {
1443   PendingGroup *pgroup;
1444   GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
1445
1446   if (priv->check_only)
1447     return;
1448
1449   g_return_if_fail (priv->groups);
1450
1451   pgroup = priv->groups->data;
1452
1453   pgroup->pending_children =
1454       g_list_prepend (pgroup->pending_children, g_strdup (child_id));
1455
1456   GST_DEBUG_OBJECT (self, "Adding %s to %s", child_id,
1457       GES_TIMELINE_ELEMENT_NAME (((PendingGroup *) priv->groups->data)->group));
1458 }