1 /* Gstreamer Editing Services
3 * Copyright (C) <2012> Thibault Saunier <thibault.saunier@collabora.com>
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.
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.
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.
26 /* TODO Determine error codes numbers */
33 #include <glib/gstdio.h>
34 #include "ges-internal.h"
36 #define parent_class ges_xml_formatter_parent_class
38 #define MINOR_VERSION 7
41 #define COLLECT_STR_OPT (G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL)
43 #define _GET_PRIV(o) (((GESXmlFormatter*)o)->priv)
56 struct _GESXmlFormatterPrivate
59 gboolean project_opened;
63 GHashTable *element_id;
64 GHashTable *subprojects_map;
65 SubprojectData *subproject;
66 gint subproject_depth;
73 G_LOCK_DEFINE_STATIC (uri_subprojects_map_lock);
74 /* { project_uri: { subproject_uri: new_suproject_uri}} */
75 static GHashTable *uri_subprojects_map = NULL;
77 G_DEFINE_TYPE_WITH_PRIVATE (GESXmlFormatter, ges_xml_formatter,
78 GES_TYPE_BASE_XML_FORMATTER);
80 static GString *_save_project (GESFormatter * formatter, GString * str,
81 GESProject * project, GESTimeline * timeline, GError ** error, guint depth);
84 _parse_ges_element (GMarkupParseContext * context, const gchar * element_name,
85 const gchar ** attribute_names, const gchar ** attribute_values,
86 GESXmlFormatter * self, GError ** error)
89 const gchar *version, *properties;
91 gchar **split_version = NULL;
93 if (g_strcmp0 (element_name, "ges")) {
94 g_set_error (error, G_MARKUP_ERROR,
95 G_MARKUP_ERROR_INVALID_CONTENT,
96 "Found element '%s', Missing '<ges>' element'", element_name);
100 if (!g_markup_collect_attributes (element_name, attribute_names,
101 attribute_values, error, G_MARKUP_COLLECT_STRING, "version", &version,
102 COLLECT_STR_OPT, "properties", &properties,
103 G_MARKUP_COLLECT_INVALID)) {
107 split_version = g_strsplit (version, ".", 2);
108 if (split_version[1] == NULL)
112 api_version = g_ascii_strtoull (split_version[0], NULL, 10);
113 if (errno || api_version != API_VERSION)
116 self->priv->min_version = g_ascii_strtoull (split_version[1], NULL, 10);
117 if (self->priv->min_version > MINOR_VERSION)
120 _GET_PRIV (self)->ges_opened = TRUE;
121 g_strfreev (split_version);
125 g_set_error (error, G_MARKUP_ERROR,
126 G_MARKUP_ERROR_INVALID_CONTENT,
127 "element '%s', %s wrong version'", element_name, version);
129 g_strfreev (split_version);
134 GST_WARNING_OBJECT (self, "Error while strtoull: %s", g_strerror (errno));
139 _parse_project (GMarkupParseContext * context, const gchar * element_name,
140 const gchar ** attribute_names, const gchar ** attribute_values,
141 GESXmlFormatter * self, GError ** error)
143 const gchar *metadatas = NULL, *properties;
144 GESXmlFormatterPrivate *priv = _GET_PRIV (self);
146 if (g_strcmp0 (element_name, "project")) {
147 g_set_error (error, G_MARKUP_ERROR,
148 G_MARKUP_ERROR_INVALID_CONTENT,
149 "Found element '%s', Missing '<project>' element'", element_name);
151 priv->project_opened = TRUE;
152 if (!g_markup_collect_attributes (element_name, attribute_names,
153 attribute_values, error,
154 COLLECT_STR_OPT, "properties", &properties,
155 COLLECT_STR_OPT, "metadatas", &metadatas, G_MARKUP_COLLECT_INVALID))
158 if (GES_FORMATTER (self)->project && metadatas)
159 ges_meta_container_add_metas_from_string (GES_META_CONTAINER
160 (GES_FORMATTER (self)->project), metadatas);
166 _parse_encoding_profile (GMarkupParseContext * context,
167 const gchar * element_name, const gchar ** attribute_names,
168 const gchar ** attribute_values, GESXmlFormatter * self, GError ** error)
170 GstCaps *capsformat = NULL;
171 GstStructure *preset_properties = NULL;
172 const gchar *name, *description, *type, *preset = NULL,
173 *str_preset_properties = NULL, *preset_name = NULL, *format;
175 if (!g_markup_collect_attributes (element_name, attribute_names,
176 attribute_values, error,
177 G_MARKUP_COLLECT_STRING, "name", &name,
178 G_MARKUP_COLLECT_STRING, "description", &description,
179 G_MARKUP_COLLECT_STRING, "type", &type,
180 COLLECT_STR_OPT, "preset", &preset,
181 COLLECT_STR_OPT, "preset-properties", &str_preset_properties,
182 COLLECT_STR_OPT, "preset-name", &preset_name,
183 COLLECT_STR_OPT, "format", &format, G_MARKUP_COLLECT_INVALID))
187 capsformat = gst_caps_from_string (format);
189 if (str_preset_properties) {
190 preset_properties = gst_structure_from_string (str_preset_properties, NULL);
191 if (preset_properties == NULL) {
192 gst_caps_unref (capsformat);
193 g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
194 "element '%s', Wrong preset-properties format.", element_name);
199 ges_base_xml_formatter_add_encoding_profile (GES_BASE_XML_FORMATTER (self),
200 type, NULL, name, description, capsformat, preset, preset_properties,
201 preset_name, 0, 0, NULL, 0, FALSE, NULL, TRUE, error);
203 if (preset_properties)
204 gst_structure_free (preset_properties);
208 _parse_stream_profile (GMarkupParseContext * context,
209 const gchar * element_name, const gchar ** attribute_names,
210 const gchar ** attribute_values, GESXmlFormatter * self, GError ** error)
212 gboolean variableframerate = FALSE, enabled = TRUE;
213 guint id = 0, presence = 0, pass = 0;
214 GstCaps *format_caps = NULL, *restriction_caps = NULL;
215 GstStructure *preset_properties = NULL;
216 const gchar *parent, *strid, *type, *strpresence, *format = NULL,
217 *name = NULL, *description = NULL, *preset,
218 *str_preset_properties = NULL, *preset_name = NULL, *restriction = NULL,
219 *strpass = NULL, *strvariableframerate = NULL, *strenabled = NULL;
221 /* FIXME Looks like there is a bug in that function, if we put the parent
222 * at the beginning it set %NULL and not the real value... :/ */
223 if (!g_markup_collect_attributes (element_name, attribute_names,
224 attribute_values, error,
225 G_MARKUP_COLLECT_STRING, "id", &strid,
226 G_MARKUP_COLLECT_STRING, "type", &type,
227 G_MARKUP_COLLECT_STRING, "presence", &strpresence,
228 COLLECT_STR_OPT, "format", &format,
229 COLLECT_STR_OPT, "name", &name,
230 COLLECT_STR_OPT, "description", &description,
231 COLLECT_STR_OPT, "preset", &preset,
232 COLLECT_STR_OPT, "preset-properties", &str_preset_properties,
233 COLLECT_STR_OPT, "preset-name", &preset_name,
234 COLLECT_STR_OPT, "restriction", &restriction,
235 COLLECT_STR_OPT, "pass", &strpass,
236 COLLECT_STR_OPT, "variableframerate", &strvariableframerate,
237 COLLECT_STR_OPT, "enabled", &strenabled,
238 G_MARKUP_COLLECT_STRING, "parent", &parent, G_MARKUP_COLLECT_INVALID))
242 id = g_ascii_strtoll (strid, NULL, 10);
244 goto convertion_failed;
247 presence = g_ascii_strtoll (strpresence, NULL, 10);
249 goto convertion_failed;
252 if (str_preset_properties) {
253 preset_properties = gst_structure_from_string (str_preset_properties, NULL);
254 if (preset_properties == NULL)
255 goto convertion_failed;
259 pass = g_ascii_strtoll (strpass, NULL, 10);
261 goto convertion_failed;
264 if (strvariableframerate) {
265 variableframerate = g_ascii_strtoll (strvariableframerate, NULL, 10);
267 goto convertion_failed;
271 enabled = g_ascii_strtoll (strenabled, NULL, 10);
273 goto convertion_failed;
277 format_caps = gst_caps_from_string (format);
280 restriction_caps = gst_caps_from_string (restriction);
282 ges_base_xml_formatter_add_encoding_profile (GES_BASE_XML_FORMATTER (self),
283 type, parent, name, description, format_caps, preset, preset_properties,
284 preset_name, id, presence, restriction_caps, pass, variableframerate,
285 NULL, enabled, error);
287 if (preset_properties)
288 gst_structure_free (preset_properties);
293 g_set_error (error, G_MARKUP_ERROR,
294 G_MARKUP_ERROR_INVALID_CONTENT,
295 "element '%s', Wrong property type, error: %s'", element_name,
301 _parse_timeline (GMarkupParseContext * context, const gchar * element_name,
302 const gchar ** attribute_names, const gchar ** attribute_values,
303 GESXmlFormatter * self, GError ** error)
305 const gchar *metadatas = NULL, *properties = NULL;
306 GESTimeline *timeline = GES_FORMATTER (self)->timeline;
308 if (!g_markup_collect_attributes (element_name, attribute_names,
309 attribute_values, error,
310 COLLECT_STR_OPT, "properties", &properties,
311 COLLECT_STR_OPT, "metadatas", &metadatas, G_MARKUP_COLLECT_INVALID))
314 if (timeline == NULL)
317 ges_base_xml_formatter_set_timeline_properties (GES_BASE_XML_FORMATTER (self),
318 timeline, properties, metadatas);
322 _parse_asset (GMarkupParseContext * context, const gchar * element_name,
323 const gchar ** attribute_names, const gchar ** attribute_values,
324 GESXmlFormatter * self, GError ** error)
326 GType extractable_type;
327 const gchar *id, *extractable_type_name, *metadatas = NULL, *properties =
328 NULL, *proxy_id = NULL;
329 GESXmlFormatterPrivate *priv = _GET_PRIV (self);
331 if (!g_markup_collect_attributes (element_name, attribute_names,
332 attribute_values, error, G_MARKUP_COLLECT_STRING, "id", &id,
333 G_MARKUP_COLLECT_STRING, "extractable-type-name",
334 &extractable_type_name,
335 COLLECT_STR_OPT, "properties", &properties,
336 COLLECT_STR_OPT, "metadatas", &metadatas,
337 COLLECT_STR_OPT, "proxy-id", &proxy_id, G_MARKUP_COLLECT_INVALID))
340 extractable_type = g_type_from_name (extractable_type_name);
341 if (extractable_type == GES_TYPE_TIMELINE) {
342 SubprojectData *subproj_data = g_malloc0 (sizeof (SubprojectData));
345 priv->subproject = subproj_data;
346 G_LOCK (uri_subprojects_map_lock);
347 nid = g_hash_table_lookup (priv->subprojects_map, id);
348 G_UNLOCK (uri_subprojects_map_lock);
351 subproj_data->id = id;
353 g_file_open_tmp ("XXXXXX.xges", &subproj_data->filename, error);
354 if (subproj_data->fd == -1) {
355 GST_ERROR_OBJECT (self, "Could not create subproject file for %s", id);
358 g_markup_parse_context_get_position (context, &subproj_data->start_line,
359 &subproj_data->start_char);
360 id = g_filename_to_uri (subproj_data->filename, NULL, NULL);
361 G_LOCK (uri_subprojects_map_lock);
362 g_hash_table_insert (priv->subprojects_map, g_strdup (subproj_data->id),
364 G_UNLOCK (uri_subprojects_map_lock);
365 GST_INFO_OBJECT (self, "Serialized subproject %sis now at: %s",
366 subproj_data->id, id);
368 GST_DEBUG_OBJECT (self, "Subproject already exists: %s -> %s", id, nid);
370 subproj_data->start_line = -1;
374 if (extractable_type == G_TYPE_NONE)
375 g_set_error (error, G_MARKUP_ERROR,
376 G_MARKUP_ERROR_INVALID_CONTENT,
377 "element '%s' invalid extractable_type %s'",
378 element_name, extractable_type_name);
379 else if (!g_type_is_a (extractable_type, GES_TYPE_EXTRACTABLE))
380 g_set_error (error, G_MARKUP_ERROR,
381 G_MARKUP_ERROR_INVALID_CONTENT,
382 "element '%s', %s not an extractable_type'",
383 element_name, extractable_type_name);
385 GstStructure *props = NULL;
387 props = gst_structure_from_string (properties, NULL);
389 if (extractable_type == GES_TYPE_URI_CLIP) {
390 G_LOCK (uri_subprojects_map_lock);
391 if (g_hash_table_contains (priv->subprojects_map, id)) {
392 id = g_hash_table_lookup (priv->subprojects_map, id);
394 GST_DEBUG_OBJECT (self, "Using subproject %s", id);
396 G_UNLOCK (uri_subprojects_map_lock);
399 ges_base_xml_formatter_add_asset (GES_BASE_XML_FORMATTER (self), id,
400 extractable_type, props, metadatas, proxy_id, error);
402 gst_structure_free (props);
408 _parse_track (GMarkupParseContext * context, const gchar * element_name,
409 const gchar ** attribute_names, const gchar ** attribute_values,
410 GESXmlFormatter * self, GError ** error)
413 GESTrackType track_type;
414 GstStructure *props = NULL;
415 const gchar *strtrack_type, *strcaps, *strtrack_id, *metadatas =
416 NULL, *properties = NULL;
418 if (!g_markup_collect_attributes (element_name, attribute_names,
419 attribute_values, error,
420 G_MARKUP_COLLECT_STRING, "track-type", &strtrack_type,
421 G_MARKUP_COLLECT_STRING, "track-id", &strtrack_id,
422 COLLECT_STR_OPT, "properties", &properties,
423 COLLECT_STR_OPT, "metadatas", &metadatas,
424 G_MARKUP_COLLECT_STRING, "caps", &strcaps, G_MARKUP_COLLECT_INVALID))
427 if ((caps = gst_caps_from_string (strcaps)) == NULL)
431 track_type = g_ascii_strtoll (strtrack_type, NULL, 10);
433 goto convertion_failed;
436 props = gst_structure_from_string (properties, NULL);
439 ges_base_xml_formatter_add_track (GES_BASE_XML_FORMATTER (self), track_type,
440 caps, strtrack_id, props, metadatas, error);
443 gst_structure_free (props);
445 gst_caps_unref (caps);
450 g_set_error (error, G_MARKUP_ERROR,
451 G_MARKUP_ERROR_INVALID_CONTENT,
452 "element '%s', Can not create caps: %s'", element_name, strcaps);
456 gst_caps_unref (caps);
457 g_set_error (error, G_MARKUP_ERROR,
458 G_MARKUP_ERROR_INVALID_CONTENT,
459 "element '%s', Wrong property type, error: %s'", element_name,
466 _parse_layer (GMarkupParseContext * context, const gchar * element_name,
467 const gchar ** attribute_names, const gchar ** attribute_values,
468 GESXmlFormatter * self, GError ** error)
470 GstStructure *props = NULL;
472 GType extractable_type = G_TYPE_NONE;
473 const gchar *metadatas = NULL, *properties = NULL, *strprio = NULL,
474 *extractable_type_name, *deactivated_tracks_str;
476 gchar **deactivated_tracks = NULL;
478 if (!g_markup_collect_attributes (element_name, attribute_names,
479 attribute_values, error,
480 G_MARKUP_COLLECT_STRING, "priority", &strprio,
481 COLLECT_STR_OPT, "extractable-type-name", &extractable_type_name,
482 COLLECT_STR_OPT, "properties", &properties,
483 COLLECT_STR_OPT, "deactivated-tracks", &deactivated_tracks_str,
484 COLLECT_STR_OPT, "metadatas", &metadatas, G_MARKUP_COLLECT_INVALID))
487 if (extractable_type_name) {
488 extractable_type = g_type_from_name (extractable_type_name);
489 if (extractable_type == G_TYPE_NONE) {
490 g_set_error (error, G_MARKUP_ERROR,
491 G_MARKUP_ERROR_INVALID_CONTENT,
492 "element '%s' invalid extractable_type %s'",
493 element_name, extractable_type_name);
496 } else if (!g_type_is_a (extractable_type, GES_TYPE_EXTRACTABLE)) {
497 g_set_error (error, G_MARKUP_ERROR,
498 G_MARKUP_ERROR_INVALID_CONTENT,
499 "element '%s', %s not an extractable_type'",
500 element_name, extractable_type_name);
507 props = gst_structure_from_string (properties, NULL);
509 goto wrong_properties;
513 priority = g_ascii_strtoll (strprio, NULL, 10);
515 goto convertion_failed;
517 if (deactivated_tracks_str)
518 deactivated_tracks = g_strsplit (deactivated_tracks_str, " ", -1);
520 ges_base_xml_formatter_add_layer (GES_BASE_XML_FORMATTER (self),
521 extractable_type, priority, props, metadatas, deactivated_tracks, error);
523 g_strfreev (deactivated_tracks);
527 gst_structure_free (props);
532 g_set_error (error, G_MARKUP_ERROR,
533 G_MARKUP_ERROR_INVALID_CONTENT,
534 "element '%s', Wrong property type, error: %s'", element_name,
539 g_set_error (error, G_MARKUP_ERROR,
540 G_MARKUP_ERROR_INVALID_CONTENT,
541 "element '%s', wrong layer properties '%s', could no be deserialized",
542 element_name, properties);
546 _parse_clip (GMarkupParseContext * context,
547 const gchar * element_name, const gchar ** attribute_names,
548 const gchar ** attribute_values, GESXmlFormatter * self, GError ** error)
551 GstStructure *props = NULL, *children_props = NULL;
552 GESTrackType track_types;
553 GstClockTime start, inpoint = 0, duration, layer_prio;
554 GESXmlFormatterPrivate *priv = _GET_PRIV (self);
556 const gchar *strid, *asset_id, *strstart, *strin, *strduration, *strrate,
557 *strtrack_types, *strtype, *metadatas = NULL, *properties =
558 NULL, *children_properties = NULL, *strlayer_prio;
560 if (!g_markup_collect_attributes (element_name, attribute_names,
561 attribute_values, error,
562 G_MARKUP_COLLECT_STRING, "id", &strid,
563 G_MARKUP_COLLECT_STRING, "type-name", &strtype,
564 G_MARKUP_COLLECT_STRING, "start", &strstart,
565 G_MARKUP_COLLECT_STRING, "duration", &strduration,
566 G_MARKUP_COLLECT_STRING, "asset-id", &asset_id,
567 G_MARKUP_COLLECT_STRING, "track-types", &strtrack_types,
568 G_MARKUP_COLLECT_STRING, "layer-priority", &strlayer_prio,
569 COLLECT_STR_OPT, "properties", &properties,
570 COLLECT_STR_OPT, "children-properties", &children_properties,
571 COLLECT_STR_OPT, "metadatas", &metadatas,
572 COLLECT_STR_OPT, "rate", &strrate,
573 COLLECT_STR_OPT, "inpoint", &strin, G_MARKUP_COLLECT_INVALID)) {
576 type = g_type_from_name (strtype);
577 if (!g_type_is_a (type, GES_TYPE_CLIP))
581 track_types = g_ascii_strtoll (strtrack_types, NULL, 10);
583 goto convertion_failed;
585 layer_prio = g_ascii_strtoll (strlayer_prio, NULL, 10);
587 goto convertion_failed;
590 inpoint = g_ascii_strtoull (strin, NULL, 10);
592 goto convertion_failed;
595 start = g_ascii_strtoull (strstart, NULL, 10);
597 goto convertion_failed;
599 duration = g_ascii_strtoull (strduration, NULL, 10);
601 goto convertion_failed;
604 props = gst_structure_from_string (properties, NULL);
606 goto wrong_properties;
609 if (children_properties) {
610 children_props = gst_structure_from_string (children_properties, NULL);
611 if (children_props == NULL)
612 goto wrong_children_properties;
615 G_LOCK (uri_subprojects_map_lock);
616 if (g_hash_table_contains (priv->subprojects_map, asset_id)) {
617 asset_id = g_hash_table_lookup (priv->subprojects_map, asset_id);
618 GST_DEBUG_OBJECT (self, "Using subproject %s", asset_id);
620 G_UNLOCK (uri_subprojects_map_lock);
621 ges_base_xml_formatter_add_clip (GES_BASE_XML_FORMATTER (self),
622 strid, asset_id, type, start, inpoint, duration, layer_prio,
623 track_types, props, children_props, metadatas, error);
625 gst_structure_free (props);
627 gst_structure_free (children_props);
632 g_set_error (error, G_MARKUP_ERROR,
633 G_MARKUP_ERROR_INVALID_CONTENT,
634 "element '%s', Clip %s properties '%s', could no be deserialized",
635 element_name, asset_id, properties);
638 wrong_children_properties:
639 g_set_error (error, G_MARKUP_ERROR,
640 G_MARKUP_ERROR_INVALID_CONTENT,
641 "element '%s', Clip %s children properties '%s', could no be deserialized",
642 element_name, asset_id, children_properties);
644 gst_structure_free (props);
648 g_set_error (error, G_MARKUP_ERROR,
649 G_MARKUP_ERROR_INVALID_CONTENT,
650 "element '%s', Wrong property type, error: %s'", element_name,
655 g_set_error (error, G_MARKUP_ERROR,
656 G_MARKUP_ERROR_INVALID_CONTENT,
657 "element '%s', %s not a GESClip'", element_name, strtype);
661 _parse_binding (GMarkupParseContext * context, const gchar * element_name,
662 const gchar ** attribute_names, const gchar ** attribute_values,
663 GESXmlFormatter * self, GError ** error)
665 const gchar *type = NULL, *source_type = NULL, *timed_values =
666 NULL, *property_name = NULL, *mode = NULL, *track_id = NULL;
667 gchar **pairs, **tmp;
671 if (!g_markup_collect_attributes (element_name, attribute_names,
672 attribute_values, error,
673 G_MARKUP_COLLECT_STRING, "type", &type,
674 G_MARKUP_COLLECT_STRING, "source_type", &source_type,
675 G_MARKUP_COLLECT_STRING, "property", &property_name,
676 G_MARKUP_COLLECT_STRING, "mode", &mode,
677 G_MARKUP_COLLECT_STRING, "track_id", &track_id,
678 G_MARKUP_COLLECT_STRING, "values", &timed_values,
679 G_MARKUP_COLLECT_INVALID)) {
683 pairs = g_strsplit (timed_values, " ", 0);
684 for (tmp = pairs; tmp != NULL; tmp += 1) {
691 GstTimedValue *value;
693 value = g_new0 (GstTimedValue, 1);
694 value_pair = g_strsplit (pair, ":", 0);
695 value->timestamp = g_ascii_strtoull (value_pair[0], NULL, 10);
696 value->value = g_ascii_strtod (value_pair[1], NULL);
697 list = g_slist_append (list, value);
698 g_strfreev (value_pair);
704 ges_base_xml_formatter_add_control_binding (GES_BASE_XML_FORMATTER (self),
707 property_name, (gint) g_ascii_strtoll (mode, NULL, 10), track_id, list);
711 _parse_source (GMarkupParseContext * context, const gchar * element_name,
712 const gchar ** attribute_names, const gchar ** attribute_values,
713 GESXmlFormatter * self, GError ** error)
715 GstStructure *children_props = NULL, *props = NULL;
716 const gchar *track_id = NULL, *children_properties = NULL, *properties = NULL;
718 if (!g_markup_collect_attributes (element_name, attribute_names,
719 attribute_values, error,
720 G_MARKUP_COLLECT_STRING, "track-id", &track_id,
721 COLLECT_STR_OPT, "children-properties", &children_properties,
722 COLLECT_STR_OPT, "properties", &properties,
723 G_MARKUP_COLLECT_INVALID)) {
727 if (children_properties) {
728 children_props = gst_structure_from_string (children_properties, NULL);
729 if (children_props == NULL)
730 goto wrong_children_properties;
734 props = gst_structure_from_string (properties, NULL);
736 goto wrong_properties;
739 ges_base_xml_formatter_add_source (GES_BASE_XML_FORMATTER (self), track_id,
740 children_props, props);
744 gst_structure_free (children_props);
747 gst_structure_free (props);
751 wrong_children_properties:
752 g_set_error (error, G_MARKUP_ERROR,
753 G_MARKUP_ERROR_INVALID_CONTENT,
754 "element '%s', children properties '%s', could no be deserialized",
755 element_name, children_properties);
759 g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
760 "element '%s', properties '%s', could no be deserialized",
761 element_name, properties);
766 _parse_effect (GMarkupParseContext * context, const gchar * element_name,
767 const gchar ** attribute_names, const gchar ** attribute_values,
768 GESXmlFormatter * self, GError ** error)
772 GstStructure *children_props = NULL, *props = NULL;
773 const gchar *asset_id = NULL, *strtype = NULL, *track_id =
774 NULL, *metadatas = NULL, *properties = NULL, *track_type = NULL,
775 *children_properties = NULL, *clip_id;
777 if (!g_markup_collect_attributes (element_name, attribute_names,
778 attribute_values, error,
779 COLLECT_STR_OPT, "metadatas", &metadatas,
780 G_MARKUP_COLLECT_STRING, "asset-id", &asset_id,
781 G_MARKUP_COLLECT_STRING, "clip-id", &clip_id,
782 G_MARKUP_COLLECT_STRING, "type-name", &strtype,
783 G_MARKUP_COLLECT_STRING, "track-id", &track_id,
784 COLLECT_STR_OPT, "children-properties", &children_properties,
785 COLLECT_STR_OPT, "track-type", &track_type,
786 COLLECT_STR_OPT, "properties", &properties,
787 G_MARKUP_COLLECT_INVALID)) {
791 type = g_type_from_name (strtype);
792 if (!g_type_is_a (type, GES_TYPE_BASE_EFFECT))
795 if (children_properties) {
796 children_props = gst_structure_from_string (children_properties, NULL);
797 if (children_props == NULL)
798 goto wrong_children_properties;
802 props = gst_structure_from_string (properties, NULL);
804 goto wrong_properties;
807 ges_base_xml_formatter_add_track_element (GES_BASE_XML_FORMATTER (self),
808 type, asset_id, track_id, clip_id, children_props, props, metadatas,
814 gst_structure_free (props);
816 gst_structure_free (children_props);
821 g_set_error (error, G_MARKUP_ERROR,
822 G_MARKUP_ERROR_INVALID_CONTENT,
823 "element '%s', Effect %s properties '%s', could no be deserialized",
824 element_name, asset_id, properties);
827 wrong_children_properties:
828 g_set_error (error, G_MARKUP_ERROR,
829 G_MARKUP_ERROR_INVALID_CONTENT,
830 "element '%s', Effect %s children properties '%s', could no be deserialized",
831 element_name, asset_id, children_properties);
835 g_set_error (error, G_MARKUP_ERROR,
836 G_MARKUP_ERROR_INVALID_CONTENT,
837 "element '%s', %s not a GESBaseEffect'", element_name, strtype);
842 _parse_group (GMarkupParseContext * context, const gchar * element_name,
843 const gchar ** attribute_names, const gchar ** attribute_values,
844 GESXmlFormatter * self, GError ** error)
846 const gchar *id, *properties, *metadatas = NULL;
848 if (!g_markup_collect_attributes (element_name, attribute_names,
849 attribute_values, error,
850 G_MARKUP_COLLECT_STRING, "id", &id,
851 G_MARKUP_COLLECT_STRING, "properties", &properties,
852 COLLECT_STR_OPT, "metadatas", &metadatas, G_MARKUP_COLLECT_INVALID)) {
856 ges_base_xml_formatter_add_group (GES_BASE_XML_FORMATTER (self), id,
857 properties, metadatas);
861 _parse_group_child (GMarkupParseContext * context, const gchar * element_name,
862 const gchar ** attribute_names, const gchar ** attribute_values,
863 GESXmlFormatter * self, GError ** error)
865 const gchar *child_id, *name;
867 if (!g_markup_collect_attributes (element_name, attribute_names,
868 attribute_values, error,
869 G_MARKUP_COLLECT_STRING, "id", &child_id,
870 G_MARKUP_COLLECT_STRING, "name", &name, G_MARKUP_COLLECT_INVALID)) {
874 ges_base_xml_formatter_last_group_add_child (GES_BASE_XML_FORMATTER (self),
879 _parse_element_start (GMarkupParseContext * context, const gchar * element_name,
880 const gchar ** attribute_names, const gchar ** attribute_values,
881 gpointer self, GError ** error)
883 GESXmlFormatterPrivate *priv = _GET_PRIV (self);
885 if (priv->subproject) {
886 if (g_strcmp0 (element_name, "ges") == 0) {
887 priv->subproject_depth += 1;
892 if (!G_UNLIKELY (priv->ges_opened)) {
893 _parse_ges_element (context, element_name, attribute_names,
894 attribute_values, self, error);
895 } else if (!G_UNLIKELY (priv->project_opened))
896 _parse_project (context, element_name, attribute_names, attribute_values,
898 else if (g_strcmp0 (element_name, "ges") == 0) {
899 } else if (g_strcmp0 (element_name, "encoding-profile") == 0)
900 _parse_encoding_profile (context, element_name, attribute_names,
901 attribute_values, self, error);
902 else if (g_strcmp0 (element_name, "stream-profile") == 0)
903 _parse_stream_profile (context, element_name, attribute_names,
904 attribute_values, self, error);
905 else if (g_strcmp0 (element_name, "timeline") == 0)
906 _parse_timeline (context, element_name, attribute_names, attribute_values,
908 else if (g_strcmp0 (element_name, "asset") == 0)
909 _parse_asset (context, element_name, attribute_names, attribute_values,
911 else if (g_strcmp0 (element_name, "track") == 0)
912 _parse_track (context, element_name, attribute_names,
913 attribute_values, self, error);
914 else if (g_strcmp0 (element_name, "layer") == 0)
915 _parse_layer (context, element_name, attribute_names,
916 attribute_values, self, error);
917 else if (g_strcmp0 (element_name, "clip") == 0)
918 _parse_clip (context, element_name, attribute_names,
919 attribute_values, self, error);
920 else if (g_strcmp0 (element_name, "source") == 0)
921 _parse_source (context, element_name, attribute_names,
922 attribute_values, self, error);
923 else if (g_strcmp0 (element_name, "effect") == 0)
924 _parse_effect (context, element_name, attribute_names,
925 attribute_values, self, error);
926 else if (g_strcmp0 (element_name, "binding") == 0)
927 _parse_binding (context, element_name, attribute_names,
928 attribute_values, self, error);
929 else if (g_strcmp0 (element_name, "group") == 0)
930 _parse_group (context, element_name, attribute_names,
931 attribute_values, self, error);
932 else if (g_strcmp0 (element_name, "child") == 0)
933 _parse_group_child (context, element_name, attribute_names,
934 attribute_values, self, error);
936 GST_LOG_OBJECT (self, "Element %s not handled", element_name);
940 _save_subproject_data (GESXmlFormatter * self, SubprojectData * subproj_data,
941 gint subproject_end_line, gint subproject_end_char, GError ** error)
945 gboolean res = FALSE;
946 gsize start = 0, end = 0;
947 gchar *subproject_content = NULL;
948 gchar *xml = GES_BASE_XML_FORMATTER (self)->xmlcontent;
950 for (i = 0; xml[i] != '\0'; i++) {
951 if (!start && line == subproj_data->start_line) {
952 i += subproj_data->start_char - 1;
956 if (line == subproject_end_line) {
957 end = i + subproject_end_char - 1;
964 g_assert (start && end);
965 size = (end - start);
967 subproject_content = g_malloc (sizeof (gchar) * size);
968 memcpy (subproject_content, &xml[start], end - start);
969 subproject_content[end - start] = '\0';
970 GST_INFO_OBJECT (self, "Saving subproject %s from %d:%d(%" G_GSIZE_FORMAT
971 ") to %d:%d(%" G_GSIZE_FORMAT ")",
972 subproj_data->id, subproj_data->start_line, subproj_data->start_char,
973 start, subproject_end_line, subproject_end_char, end);
975 res = g_file_set_contents (subproj_data->filename, subproject_content, -1,
977 g_free (subproject_content);
983 _parse_element_end (GMarkupParseContext * context,
984 const gchar * element_name, gpointer self, GError ** error)
986 GESXmlFormatterPrivate *priv = _GET_PRIV (self);
987 SubprojectData *subproj_data = priv->subproject;
989 /*GESXmlFormatterPrivate *priv = _GET_PRIV (self); */
991 if (!g_strcmp0 (element_name, "ges")) {
992 gint subproject_end_line, subproject_end_char;
994 if (priv->subproject_depth)
995 priv->subproject_depth -= 1;
998 if (GES_FORMATTER (self)->project) {
999 gchar *version = g_strdup_printf ("%d.%d",
1000 API_VERSION, GES_XML_FORMATTER (self)->priv->min_version);
1002 ges_meta_container_set_string (GES_META_CONTAINER (GES_FORMATTER
1003 (self)->project), GES_META_FORMAT_VERSION, version);
1006 _GET_PRIV (self)->ges_opened = FALSE;
1008 } else if (subproj_data->start_line != -1 && !priv->subproject_depth) {
1009 g_markup_parse_context_get_position (context, &subproject_end_line,
1010 &subproject_end_char);
1011 _save_subproject_data (GES_XML_FORMATTER (self), subproj_data,
1012 subproject_end_line, subproject_end_char, error);
1014 subproj_data->filename = NULL;
1015 g_close (subproj_data->fd, error);
1016 subproj_data->id = NULL;
1017 subproj_data->start_line = 0;
1018 subproj_data->start_char = 0;
1021 if (!priv->subproject_depth) {
1022 g_clear_pointer (&priv->subproject, g_free);
1024 } else if (!g_strcmp0 (element_name, "clip")) {
1025 if (!priv->subproject)
1026 ges_base_xml_formatter_end_current_clip (GES_BASE_XML_FORMATTER (self));
1031 _error_parsing (GMarkupParseContext * context, GError * error,
1034 GST_WARNING ("Error occurred when parsing %s", error->message);
1037 /***********************************************
1039 * Saving implementation *
1041 ***********************************************/
1043 /* XML writting utils */
1045 string_add_indents (GString * str, guint depth, gboolean prepend)
1048 for (i = 0; i < depth; i++)
1049 prepend ? g_string_prepend (str, " ") : g_string_append (str, " ");
1053 string_append_with_depth (GString * str, const gchar * string, guint depth)
1055 string_add_indents (str, depth, FALSE);
1056 g_string_append (str, string);
1060 append_escaped (GString * str, gchar * tmpstr, guint depth)
1062 string_append_with_depth (str, tmpstr, depth);
1067 ges_util_can_serialize_spec (GParamSpec * spec)
1069 if (!(spec->flags & G_PARAM_WRITABLE)) {
1070 GST_LOG ("%s from %s is not writable",
1071 spec->name, g_type_name (spec->owner_type));
1074 } else if (spec->flags & G_PARAM_CONSTRUCT_ONLY) {
1075 GST_LOG ("%s from %s is construct only",
1076 spec->name, g_type_name (spec->owner_type));
1079 } else if (spec->flags & GES_PARAM_NO_SERIALIZATION &&
1080 g_type_is_a (spec->owner_type, GES_TYPE_TIMELINE_ELEMENT)) {
1081 GST_LOG ("%s from %s is set as GES_PARAM_NO_SERIALIZATION",
1082 spec->name, g_type_name (spec->owner_type));
1085 } else if (g_type_is_a (G_PARAM_SPEC_VALUE_TYPE (spec), G_TYPE_OBJECT)) {
1086 GST_LOG ("%s from %s contains GObject, can't serialize that.",
1087 spec->name, g_type_name (spec->owner_type));
1090 } else if ((g_type_is_a (spec->owner_type, GST_TYPE_OBJECT) &&
1091 !g_strcmp0 (spec->name, "name"))) {
1093 GST_LOG ("We do not want to serialize the name of GstObjects.");
1095 } else if (G_PARAM_SPEC_VALUE_TYPE (spec) == G_TYPE_GTYPE) {
1096 GST_LOG ("%s from %s contains a GType, can't serialize.",
1097 spec->name, g_type_name (spec->owner_type));
1105 _init_value_from_spec_for_serialization (GValue * value, GParamSpec * spec)
1108 if (g_type_is_a (spec->value_type, G_TYPE_ENUM) ||
1109 g_type_is_a (spec->value_type, G_TYPE_FLAGS))
1110 g_value_init (value, G_TYPE_INT);
1112 g_value_init (value, spec->value_type);
1116 _serialize_properties (GObject * object, gint * ret_n_props,
1117 const gchar * fieldname, ...)
1121 GParamSpec *spec, **pspecs;
1122 GObjectClass *class = G_OBJECT_GET_CLASS (object);
1123 GstStructure *structure = gst_structure_new_empty ("properties");
1125 pspecs = g_object_class_list_properties (class, &n_props);
1126 for (j = 0; j < n_props; j++) {
1130 if (!ges_util_can_serialize_spec (spec))
1133 _init_value_from_spec_for_serialization (&val, spec);
1134 g_object_get_property (object, spec->name, &val);
1135 if (gst_value_compare (g_param_spec_get_default_value (spec),
1136 &val) == GST_VALUE_EQUAL) {
1137 GST_INFO ("Ignoring %s as it is using the default value", spec->name);
1141 if (spec->value_type == GST_TYPE_CAPS) {
1143 const GstCaps *caps = gst_value_get_caps (&val);
1145 caps_str = gst_caps_to_string (caps);
1146 gst_structure_set (structure, spec->name, G_TYPE_STRING, caps_str, NULL);
1151 gst_structure_set_value (structure, spec->name, &val);
1154 g_value_unset (&val);
1160 va_start (varargs, fieldname);
1161 gst_structure_remove_fields_valist (structure, fieldname, varargs);
1165 ret = gst_structure_to_string (structure);
1167 *ret_n_props = gst_structure_n_fields (structure);
1168 gst_structure_free (structure);
1174 project_loaded_cb (GESProject * project, GESTimeline * timeline,
1175 SubprojectData * data)
1177 g_main_loop_quit (data->ml);
1181 error_loading_asset_cb (GESProject * project, GError * err,
1182 const gchar * unused_id, GType extractable_type, SubprojectData * data)
1184 data->error = g_error_copy (err);
1185 g_main_loop_quit (data->ml);
1189 _save_subproject (GESXmlFormatter * self, GString * str, GESProject * project,
1190 GESAsset * subproject, GError ** error, guint depth)
1193 GESTimeline *timeline;
1194 gchar *properties, *metas;
1195 GESXmlFormatterPrivate *priv = self->priv;
1196 GMainContext *context = g_main_context_get_thread_default ();
1197 const gchar *id = ges_asset_get_id (subproject);
1198 SubprojectData data = { 0, };
1200 if (!g_strcmp0 (ges_asset_get_id (GES_ASSET (project)), id)) {
1201 g_set_error (error, G_MARKUP_ERROR,
1202 G_MARKUP_ERROR_INVALID_CONTENT,
1203 "Project %s trying to recurse into itself", id);
1207 G_LOCK (uri_subprojects_map_lock);
1208 g_hash_table_insert (priv->subprojects_map, g_strdup (id), g_strdup (id));
1209 G_UNLOCK (uri_subprojects_map_lock);
1210 timeline = GES_TIMELINE (ges_asset_extract (subproject, error));
1216 context = g_main_context_default ();
1218 data.ml = g_main_loop_new (context, TRUE);
1219 g_signal_connect (subproject, "loaded", (GCallback) project_loaded_cb, &data);
1220 g_signal_connect (subproject, "error-loading-asset",
1221 (GCallback) error_loading_asset_cb, &data);
1222 g_main_loop_run (data.ml);
1224 g_signal_handlers_disconnect_by_func (subproject, project_loaded_cb, &data);
1225 g_signal_handlers_disconnect_by_func (subproject, error_loading_asset_cb,
1228 g_propagate_error (error, data.error);
1232 subproject = ges_extractable_get_asset (GES_EXTRACTABLE (timeline));
1233 substr = g_string_new (NULL);
1234 properties = _serialize_properties (G_OBJECT (subproject), NULL, NULL);
1235 metas = ges_meta_container_metas_to_string (GES_META_CONTAINER (subproject));
1236 append_escaped (str,
1237 g_markup_printf_escaped
1238 (" <asset id='%s' extractable-type-name='%s' properties='%s' metadatas='%s'>\n",
1239 ges_asset_get_id (subproject),
1240 g_type_name (ges_asset_get_extractable_type (subproject)), properties,
1242 self->priv->min_version = MAX (self->priv->min_version, 6);
1245 GST_DEBUG_OBJECT (self, "Saving subproject %s (depth: %d)",
1246 ges_asset_get_id (subproject), depth / 4);
1247 if (!_save_project (GES_FORMATTER (self), substr, GES_PROJECT (subproject),
1248 timeline, error, depth)) {
1249 g_string_free (substr, TRUE);
1250 g_object_unref (subproject);
1253 GST_DEBUG_OBJECT (self, "DONE Saving subproject %s",
1254 ges_asset_get_id (subproject));
1257 g_string_append (str, substr->str);
1258 g_string_free (substr, TRUE);
1259 string_append_with_depth (str, " </asset>\n", depth);
1262 g_object_unref (subproject);
1268 sort_assets (GESAsset * a, GESAsset * b)
1270 if (GES_IS_PROJECT (a))
1273 if (GES_IS_PROJECT (b))
1280 _serialize_streams (GESXmlFormatter * self, GString * str,
1281 GESUriClipAsset * asset, GError ** error, guint depth)
1283 const GList *tmp, *streams = ges_uri_clip_asset_get_stream_assets (asset);
1285 for (tmp = streams; tmp; tmp = tmp->next) {
1286 gchar *properties, *metas, *capsstr;
1287 const gchar *id = ges_asset_get_id (tmp->data);
1288 GstDiscovererStreamInfo *sinfo =
1289 ges_uri_source_asset_get_stream_info (tmp->data);
1290 GstCaps *caps = gst_discoverer_stream_info_get_caps (sinfo);
1292 properties = _serialize_properties (tmp->data, NULL, NULL);
1293 metas = ges_meta_container_metas_to_string (tmp->data);
1294 capsstr = gst_caps_to_string (caps);
1296 append_escaped (str,
1297 g_markup_printf_escaped
1298 (" <stream-info id='%s' extractable-type-name='%s' properties='%s' metadatas='%s' caps='%s'/>\n",
1299 id, g_type_name (ges_asset_get_extractable_type (tmp->data)),
1300 properties, metas, capsstr), depth);
1301 self->priv->min_version = MAX (self->priv->min_version, 6);
1303 g_free (properties);
1305 gst_caps_unref (caps);
1310 static inline gboolean
1311 _save_assets (GESXmlFormatter * self, GString * str, GESProject * project,
1312 GError ** error, guint depth)
1314 gchar *properties, *metas;
1315 GESAsset *asset, *proxy;
1316 GList *assets, *tmp;
1318 GESXmlFormatterPrivate *priv = self->priv;
1320 assets = ges_project_list_assets (project, GES_TYPE_EXTRACTABLE);
1321 for (tmp = g_list_sort (assets, (GCompareFunc) sort_assets); tmp;
1323 asset = GES_ASSET (tmp->data);
1324 id = ges_asset_get_id (asset);
1326 if (GES_IS_PROJECT (asset)) {
1327 if (!_save_subproject (self, str, project, asset, error, depth))
1333 if (ges_asset_get_extractable_type (asset) == GES_TYPE_URI_CLIP) {
1334 G_LOCK (uri_subprojects_map_lock);
1335 if (g_hash_table_contains (priv->subprojects_map, id)) {
1336 id = g_hash_table_lookup (priv->subprojects_map, id);
1338 GST_DEBUG_OBJECT (self, "Using subproject %s", id);
1340 G_UNLOCK (uri_subprojects_map_lock);
1343 properties = _serialize_properties (G_OBJECT (asset), NULL, NULL);
1344 metas = ges_meta_container_metas_to_string (GES_META_CONTAINER (asset));
1345 append_escaped (str,
1346 g_markup_printf_escaped
1347 (" <asset id='%s' extractable-type-name='%s' properties='%s' metadatas='%s' ",
1348 id, g_type_name (ges_asset_get_extractable_type (asset)),
1349 properties, metas), depth);
1351 /*TODO Save the whole list of proxies */
1352 proxy = ges_asset_get_proxy (asset);
1354 const gchar *proxy_id = ges_asset_get_id (proxy);
1356 if (ges_asset_get_extractable_type (asset) == GES_TYPE_URI_CLIP) {
1357 G_LOCK (uri_subprojects_map_lock);
1358 if (g_hash_table_contains (priv->subprojects_map, proxy_id)) {
1359 proxy_id = g_hash_table_lookup (priv->subprojects_map, proxy_id);
1361 GST_DEBUG_OBJECT (self, "Using subproject %s", id);
1363 G_UNLOCK (uri_subprojects_map_lock);
1365 append_escaped (str, g_markup_printf_escaped (" proxy-id='%s' ",
1368 if (!g_list_find (assets, proxy)) {
1369 assets = g_list_append (assets, gst_object_ref (proxy));
1372 tmp->next = g_list_last (assets);
1375 self->priv->min_version = MAX (self->priv->min_version, 3);
1377 g_string_append (str, ">\n");
1379 if (GES_IS_URI_CLIP_ASSET (asset)) {
1380 _serialize_streams (self, str, GES_URI_CLIP_ASSET (asset), error, depth);
1383 string_append_with_depth (str, " </asset>\n", depth);
1384 g_free (properties);
1388 g_list_free_full (assets, gst_object_unref);
1394 _save_tracks (GESXmlFormatter * self, GString * str, GESTimeline * timeline,
1397 gchar *strtmp, *metas;
1399 GList *tmp, *tracks;
1402 guint nb_tracks = 0;
1404 tracks = ges_timeline_get_tracks (timeline);
1405 for (tmp = tracks; tmp; tmp = tmp->next) {
1406 track = GES_TRACK (tmp->data);
1407 properties = _serialize_properties (G_OBJECT (track), NULL, "caps", NULL);
1408 strtmp = gst_caps_to_string (ges_track_get_caps (track));
1409 metas = ges_meta_container_metas_to_string (GES_META_CONTAINER (track));
1410 append_escaped (str,
1411 g_markup_printf_escaped
1412 (" <track caps='%s' track-type='%i' track-id='%i' properties='%s' metadatas='%s'/>\n",
1413 strtmp, track->type, nb_tracks++, properties, metas), depth);
1416 g_free (properties);
1418 g_list_free_full (tracks, gst_object_unref);
1422 _save_children_properties (GString * str, GESTimelineElement * element,
1425 GstStructure *structure;
1426 GParamSpec **pspecs, *spec;
1430 pspecs = ges_timeline_element_list_children_properties (element, &n_props);
1432 structure = gst_structure_new_empty ("properties");
1433 for (i = 0; i < n_props; i++) {
1437 if (ges_util_can_serialize_spec (spec)) {
1439 g_strdup_printf ("%s::%s", g_type_name (spec->owner_type),
1442 _init_value_from_spec_for_serialization (&val, spec);
1443 ges_timeline_element_get_child_property_by_pspec (element, spec, &val);
1444 gst_structure_set_value (structure, spec_name, &val);
1447 g_value_unset (&val);
1449 g_param_spec_unref (spec);
1453 struct_str = gst_structure_to_string (structure);
1454 append_escaped (str,
1455 g_markup_printf_escaped (" children-properties='%s'", struct_str), 0);
1456 gst_structure_free (structure);
1457 g_free (struct_str);
1460 /* TODO : Use this function for every track element with controllable properties */
1462 _save_keyframes (GString * str, GESTrackElement * trackelement, gint index,
1465 GHashTable *bindings_hashtable;
1466 GHashTableIter iter;
1467 gpointer key, value;
1469 bindings_hashtable =
1470 ges_track_element_get_all_control_bindings (trackelement);
1472 g_hash_table_iter_init (&iter, bindings_hashtable);
1474 /* We iterate over the bindings, and save the timed values */
1475 while (g_hash_table_iter_next (&iter, &key, &value)) {
1476 if (GST_IS_DIRECT_CONTROL_BINDING ((GstControlBinding *) value)) {
1477 GstControlSource *source;
1478 gboolean absolute = FALSE;
1479 GstDirectControlBinding *binding;
1481 binding = (GstDirectControlBinding *) value;
1483 g_object_get (binding, "control-source", &source,
1484 "absolute", &absolute, NULL);
1486 if (GST_IS_INTERPOLATION_CONTROL_SOURCE (source)) {
1487 GList *timed_values, *tmp;
1488 GstInterpolationMode mode;
1490 append_escaped (str,
1491 g_markup_printf_escaped
1492 (" <binding type='%s' source_type='interpolation' property='%s'",
1493 absolute ? "direct-absolute" : "direct", (gchar *) key), depth);
1495 g_object_get (source, "mode", &mode, NULL);
1496 append_escaped (str, g_markup_printf_escaped (" mode='%d'", mode),
1498 append_escaped (str, g_markup_printf_escaped (" track_id='%d'", index),
1500 append_escaped (str, g_markup_printf_escaped (" values ='"), depth);
1502 gst_timed_value_control_source_get_all
1503 (GST_TIMED_VALUE_CONTROL_SOURCE (source));
1504 for (tmp = timed_values; tmp; tmp = tmp->next) {
1505 gchar strbuf[G_ASCII_DTOSTR_BUF_SIZE];
1506 GstTimedValue *value;
1508 value = (GstTimedValue *) tmp->data;
1509 append_escaped (str, g_markup_printf_escaped (" %" G_GUINT64_FORMAT
1510 ":%s ", value->timestamp, g_ascii_dtostr (strbuf,
1511 G_ASCII_DTOSTR_BUF_SIZE, value->value)), depth);
1513 g_list_free (timed_values);
1514 append_escaped (str, g_markup_printf_escaped ("'/>\n"), depth);
1516 GST_DEBUG ("control source not in [interpolation]");
1518 gst_object_unref (source);
1520 GST_DEBUG ("Binding type not in [direct, direct-absolute]");
1525 _save_effect (GString * str, guint clip_id, GESTrackElement * trackelement,
1526 GESTimeline * timeline, guint depth)
1529 GList *tmp, *tracks;
1530 gchar *properties, *metas;
1533 gchar *extractable_id;
1535 g_object_get (trackelement, "serialize", &serialize, NULL);
1538 GST_DEBUG_OBJECT (trackelement, "Should not be serialized");
1543 tck = ges_track_element_get_track (trackelement);
1545 GST_WARNING_OBJECT (trackelement, " Not in any track, can not save it");
1550 tracks = ges_timeline_get_tracks (timeline);
1551 for (tmp = tracks; tmp; tmp = tmp->next) {
1552 if (tmp->data == tck)
1556 g_list_free_full (tracks, gst_object_unref);
1558 properties = _serialize_properties (G_OBJECT (trackelement), NULL, "start",
1559 "duration", "locked", "name", "priority", NULL);
1561 ges_meta_container_metas_to_string (GES_META_CONTAINER (trackelement));
1562 extractable_id = ges_extractable_get_id (GES_EXTRACTABLE (trackelement));
1563 append_escaped (str,
1564 g_markup_printf_escaped (" <effect asset-id='%s' clip-id='%u'"
1565 " type-name='%s' track-type='%i' track-id='%i' properties='%s' metadatas='%s'",
1566 extractable_id, clip_id,
1567 g_type_name (G_OBJECT_TYPE (trackelement)), tck->type, track_id,
1568 properties, metas), depth);
1569 g_free (extractable_id);
1570 g_free (properties);
1573 _save_children_properties (str, GES_TIMELINE_ELEMENT (trackelement), depth);
1574 append_escaped (str, g_markup_printf_escaped (">\n"), depth);
1576 _save_keyframes (str, trackelement, -1, depth);
1578 append_escaped (str, g_markup_printf_escaped (" </effect>\n"),
1583 _save_layer_track_activness (GESXmlFormatter * self, GESLayer * layer,
1584 GString * str, GESTimeline * timeline, guint depth)
1586 guint nb_tracks = 0, i;
1587 GList *tmp, *tracks = ges_timeline_get_tracks (timeline);
1588 GArray *deactivated_tracks = g_array_new (TRUE, FALSE, sizeof (gint32));
1590 for (tmp = tracks; tmp; tmp = tmp->next, nb_tracks++) {
1591 if (!ges_layer_get_active_for_track (layer, tmp->data))
1592 g_array_append_val (deactivated_tracks, nb_tracks);
1595 if (!deactivated_tracks->len) {
1596 g_string_append (str, ">\n");
1600 self->priv->min_version = MAX (self->priv->min_version, 7);
1601 g_string_append (str, " deactivated-tracks='");
1602 for (i = 0; i < deactivated_tracks->len; i++)
1603 g_string_append_printf (str, "%d ", g_array_index (deactivated_tracks, gint,
1605 g_string_append (str, "'>\n");
1608 g_array_free (deactivated_tracks, TRUE);
1609 g_list_free_full (tracks, gst_object_unref);
1613 _save_source (GESXmlFormatter * self, GString * str,
1614 GESTimelineElement * element, GESTimeline * timeline, GList * tracks,
1617 gint index, n_props;
1621 if (!GES_IS_SOURCE (element))
1624 g_object_get (element, "serialize", &serialize, NULL);
1626 GST_DEBUG_OBJECT (element, "Should not be serialized");
1631 g_list_index (tracks,
1632 ges_track_element_get_track (GES_TRACK_ELEMENT (element)));
1633 append_escaped (str,
1634 g_markup_printf_escaped
1635 (" <source track-id='%i' ", index), depth);
1637 properties = _serialize_properties (G_OBJECT (element), &n_props,
1638 "in-point", "priority", "start", "duration", "track", "track-type"
1639 "uri", "name", "max-duration", NULL);
1641 /* Try as possible to allow older versions of GES to load the files */
1643 self->priv->min_version = MAX (self->priv->min_version, 7);
1644 g_string_append_printf (str, "properties='%s' ", properties);
1646 g_free (properties);
1648 _save_children_properties (str, element, depth);
1649 append_escaped (str, g_markup_printf_escaped (">\n"), depth);
1650 _save_keyframes (str, GES_TRACK_ELEMENT (element), index, depth);
1651 append_escaped (str, g_markup_printf_escaped (" </source>\n"),
1656 _save_layers (GESXmlFormatter * self, GString * str, GESTimeline * timeline,
1659 gchar *properties, *metas;
1662 GList *tmplayer, *tmpclip, *clips;
1663 GESXmlFormatterPrivate *priv = self->priv;
1665 for (tmplayer = timeline->layers; tmplayer; tmplayer = tmplayer->next) {
1667 layer = GES_LAYER (tmplayer->data);
1669 priority = ges_layer_get_priority (layer);
1671 _serialize_properties (G_OBJECT (layer), NULL, "priority", NULL);
1672 metas = ges_meta_container_metas_to_string (GES_META_CONTAINER (layer));
1673 append_escaped (str,
1674 g_markup_printf_escaped
1675 (" <layer priority='%i' properties='%s' metadatas='%s'",
1676 priority, properties, metas), depth);
1677 g_free (properties);
1680 _save_layer_track_activness (self, layer, str, timeline, depth);
1682 clips = ges_layer_get_clips (layer);
1683 for (tmpclip = clips; tmpclip; tmpclip = tmpclip->next) {
1684 GList *effects, *tmpeffect;
1685 GList *tmptrackelement;
1688 gchar *extractable_id;
1690 clip = GES_CLIP (tmpclip->data);
1692 g_object_get (clip, "serialize", &serialize, NULL);
1694 GST_DEBUG_OBJECT (clip, "Should not be serialized");
1698 /* We escape all mandatrorry properties that are handled sparetely
1699 * and vtype for StandarTransition as it is the asset ID */
1700 properties = _serialize_properties (G_OBJECT (clip), NULL,
1701 "supported-formats", "rate", "in-point", "start", "duration",
1702 "max-duration", "priority", "vtype", "uri", NULL);
1703 extractable_id = ges_extractable_get_id (GES_EXTRACTABLE (clip));
1704 if (GES_IS_URI_CLIP (clip)) {
1705 G_LOCK (uri_subprojects_map_lock);
1706 if (g_hash_table_contains (priv->subprojects_map, extractable_id))
1708 g_strdup (g_hash_table_lookup (priv->subprojects_map,
1710 G_UNLOCK (uri_subprojects_map_lock);
1712 metas = ges_meta_container_metas_to_string (GES_META_CONTAINER (clip));
1713 append_escaped (str,
1714 g_markup_printf_escaped (" <clip id='%i' asset-id='%s'"
1715 " type-name='%s' layer-priority='%i' track-types='%i' start='%"
1716 G_GUINT64_FORMAT "' duration='%" G_GUINT64_FORMAT "' inpoint='%"
1717 G_GUINT64_FORMAT "' rate='%d' properties='%s' metadatas='%s'",
1718 priv->nbelements, extractable_id,
1719 g_type_name (G_OBJECT_TYPE (clip)), priority,
1720 ges_clip_get_supported_formats (clip), _START (clip),
1721 _DURATION (clip), _INPOINT (clip), 0, properties, metas), depth);
1724 if (GES_IS_TRANSITION_CLIP (clip)) {
1725 _save_children_properties (str, GES_TIMELINE_ELEMENT (clip), depth);
1726 self->priv->min_version = MAX (self->priv->min_version, 4);
1728 g_string_append (str, ">\n");
1730 g_free (extractable_id);
1731 g_free (properties);
1733 g_hash_table_insert (self->priv->element_id, clip,
1734 GINT_TO_POINTER (priv->nbelements));
1737 /* Effects must always be serialized in the right priority order.
1738 * List order is guaranteed by the fact that ges_clip_get_top_effects
1739 * sorts the effects. */
1740 effects = ges_clip_get_top_effects (clip);
1741 for (tmpeffect = effects; tmpeffect; tmpeffect = tmpeffect->next) {
1742 _save_effect (str, priv->nbelements,
1743 GES_TRACK_ELEMENT (tmpeffect->data), timeline, depth);
1745 g_list_free (effects);
1746 tracks = ges_timeline_get_tracks (timeline);
1748 for (tmptrackelement = GES_CONTAINER_CHILDREN (clip); tmptrackelement;
1749 tmptrackelement = tmptrackelement->next) {
1750 _save_source (self, str, tmptrackelement->data, timeline, tracks,
1753 g_list_free_full (tracks, gst_object_unref);
1755 string_append_with_depth (str, " </clip>\n", depth);
1759 g_list_free_full (clips, (GDestroyNotify) gst_object_unref);
1760 string_append_with_depth (str, " </layer>\n", depth);
1765 _save_group (GESXmlFormatter * self, GString * str, GList ** seen_groups,
1766 GESGroup * group, guint depth)
1770 gchar *properties, *metadatas;
1772 g_object_get (group, "serialize", &serialize, NULL);
1775 GST_DEBUG_OBJECT (group, "Should not be serialized");
1780 if (g_list_find (*seen_groups, group)) {
1781 GST_DEBUG_OBJECT (group, "Already serialized");
1786 *seen_groups = g_list_prepend (*seen_groups, group);
1787 for (tmp = GES_CONTAINER_CHILDREN (group); tmp; tmp = tmp->next) {
1788 if (GES_IS_GROUP (tmp->data)) {
1789 _save_group (self, str, seen_groups,
1790 GES_GROUP (GES_TIMELINE_ELEMENT (tmp->data)), depth);
1794 properties = _serialize_properties (G_OBJECT (group), NULL, NULL);
1796 metadatas = ges_meta_container_metas_to_string (GES_META_CONTAINER (group));
1797 self->priv->min_version = MAX (self->priv->min_version, 5);
1799 string_add_indents (str, depth, FALSE);
1800 g_string_append_printf (str,
1801 " <group id='%d' properties='%s' metadatas='%s'>\n",
1802 self->priv->nbelements, properties, metadatas);
1803 g_free (properties);
1805 g_hash_table_insert (self->priv->element_id, group,
1806 GINT_TO_POINTER (self->priv->nbelements));
1807 self->priv->nbelements++;
1809 for (tmp = GES_CONTAINER_CHILDREN (group); tmp; tmp = tmp->next) {
1810 gint id = GPOINTER_TO_INT (g_hash_table_lookup (self->priv->element_id,
1813 string_add_indents (str, depth, FALSE);
1814 g_string_append_printf (str, " <child id='%d' name='%s'/>\n", id,
1815 GES_TIMELINE_ELEMENT_NAME (tmp->data));
1817 string_append_with_depth (str, " </group>\n", depth);
1821 _save_groups (GESXmlFormatter * self, GString * str, GESTimeline * timeline,
1825 GList *seen_groups = NULL;
1827 string_append_with_depth (str, " <groups>\n", depth);
1828 for (tmp = ges_timeline_get_groups (timeline); tmp; tmp = tmp->next) {
1829 _save_group (self, str, &seen_groups, tmp->data, depth);
1831 g_list_free (seen_groups);
1832 string_append_with_depth (str, " </groups>\n", depth);
1836 _save_timeline (GESXmlFormatter * self, GString * str, GESTimeline * timeline,
1839 gchar *properties = NULL, *metas = NULL;
1842 _serialize_properties (G_OBJECT (timeline), NULL, "update", "name",
1843 "async-handling", "message-forward", NULL);
1845 ges_meta_container_set_uint64 (GES_META_CONTAINER (timeline), "duration",
1846 ges_timeline_get_duration (timeline));
1847 metas = ges_meta_container_metas_to_string (GES_META_CONTAINER (timeline));
1848 append_escaped (str,
1849 g_markup_printf_escaped
1850 (" <timeline properties='%s' metadatas='%s'>\n", properties, metas),
1853 _save_tracks (self, str, timeline, depth);
1854 _save_layers (self, str, timeline, depth);
1855 _save_groups (self, str, timeline, depth);
1857 string_append_with_depth (str, " </timeline>\n", depth);
1859 g_free (properties);
1864 _save_stream_profiles (GESXmlFormatter * self, GString * str,
1865 GstEncodingProfile * sprof, const gchar * profilename, guint id,
1870 GstStructure *properties;
1871 const gchar *preset, *preset_name, *name, *description;
1873 append_escaped (str,
1874 g_markup_printf_escaped
1875 (" <stream-profile parent='%s' id='%d' type='%s' "
1876 "presence='%d' ", profilename, id,
1877 gst_encoding_profile_get_type_nick (sprof),
1878 gst_encoding_profile_get_presence (sprof)), depth);
1880 if (!gst_encoding_profile_is_enabled (sprof)) {
1881 append_escaped (str, g_strdup ("enabled='0' "), depth);
1883 self->priv->min_version = MAX (self->priv->min_version, 2);
1886 tmpcaps = gst_encoding_profile_get_format (sprof);
1888 tmpc = gst_caps_to_string (tmpcaps);
1889 append_escaped (str, g_markup_printf_escaped ("format='%s' ", tmpc), depth);
1890 gst_caps_unref (tmpcaps);
1894 name = gst_encoding_profile_get_name (sprof);
1896 append_escaped (str, g_markup_printf_escaped ("name='%s' ", name), depth);
1898 description = gst_encoding_profile_get_description (sprof);
1900 append_escaped (str, g_markup_printf_escaped ("description='%s' ",
1901 description), depth);
1903 preset = gst_encoding_profile_get_preset (sprof);
1905 append_escaped (str, g_markup_printf_escaped ("preset='%s' ", preset),
1909 properties = gst_encoding_profile_get_element_properties (sprof);
1911 gchar *props_str = gst_structure_to_string (properties);
1913 append_escaped (str,
1914 g_markup_printf_escaped ("preset-properties='%s' ", props_str), depth);
1916 gst_structure_free (properties);
1919 preset_name = gst_encoding_profile_get_preset_name (sprof);
1921 append_escaped (str, g_markup_printf_escaped ("preset-name='%s' ",
1922 preset_name), depth);
1924 tmpcaps = gst_encoding_profile_get_restriction (sprof);
1926 tmpc = gst_caps_to_string (tmpcaps);
1927 append_escaped (str, g_markup_printf_escaped ("restriction='%s' ", tmpc),
1929 gst_caps_unref (tmpcaps);
1933 if (GST_IS_ENCODING_VIDEO_PROFILE (sprof)) {
1934 GstEncodingVideoProfile *vp = (GstEncodingVideoProfile *) sprof;
1936 append_escaped (str,
1937 g_markup_printf_escaped ("pass='%d' variableframerate='%i' ",
1938 gst_encoding_video_profile_get_pass (vp),
1939 gst_encoding_video_profile_get_variableframerate (vp)), depth);
1942 g_string_append (str, "/>\n");
1946 _save_encoding_profiles (GESXmlFormatter * self, GString * str,
1947 GESProject * project, guint depth)
1949 GstCaps *profformat;
1950 GstStructure *properties;
1951 const gchar *profname, *profdesc, *profpreset, *proftype, *profpresetname;
1954 GList *profiles = g_list_reverse (g_list_copy ((GList *)
1955 ges_project_list_encoding_profiles (project)));
1957 for (tmp = profiles; tmp; tmp = tmp->next) {
1958 GstEncodingProfile *prof = GST_ENCODING_PROFILE (tmp->data);
1960 profname = gst_encoding_profile_get_name (prof);
1961 profdesc = gst_encoding_profile_get_description (prof);
1962 profpreset = gst_encoding_profile_get_preset (prof);
1963 profpresetname = gst_encoding_profile_get_preset_name (prof);
1964 proftype = gst_encoding_profile_get_type_nick (prof);
1966 append_escaped (str,
1967 g_markup_printf_escaped
1968 (" <encoding-profile name='%s' description='%s' type='%s' ",
1969 profname, profdesc, proftype), depth);
1972 append_escaped (str, g_markup_printf_escaped ("preset='%s' ",
1973 profpreset), depth);
1976 properties = gst_encoding_profile_get_element_properties (prof);
1978 gchar *props_str = gst_structure_to_string (properties);
1980 append_escaped (str,
1981 g_markup_printf_escaped ("preset-properties='%s' ", props_str),
1984 gst_structure_free (properties);
1988 append_escaped (str, g_markup_printf_escaped ("preset-name='%s' ",
1989 profpresetname), depth);
1991 profformat = gst_encoding_profile_get_format (prof);
1993 gchar *format = gst_caps_to_string (profformat);
1994 append_escaped (str, g_markup_printf_escaped ("format='%s' ", format),
1997 gst_caps_unref (profformat);
2000 g_string_append (str, ">\n");
2002 if (GST_IS_ENCODING_CONTAINER_PROFILE (prof)) {
2005 GstEncodingContainerProfile *container_prof;
2007 container_prof = GST_ENCODING_CONTAINER_PROFILE (prof);
2008 for (tmp2 = gst_encoding_container_profile_get_profiles (container_prof);
2009 tmp2; tmp2 = tmp2->next, i++) {
2010 GstEncodingProfile *sprof = (GstEncodingProfile *) tmp2->data;
2011 _save_stream_profiles (self, str, sprof, profname, i, depth);
2014 append_escaped (str,
2015 g_markup_printf_escaped (" </encoding-profile>\n"), depth);
2017 g_list_free (profiles);
2021 _save (GESFormatter * formatter, GESTimeline * timeline, GError ** error)
2024 GESProject *project;
2025 GESXmlFormatterPrivate *priv = _GET_PRIV (formatter);
2027 priv->min_version = 1;
2028 project = formatter->project;
2029 str = priv->str = g_string_new (NULL);
2031 return _save_project (formatter, str, project, timeline, error, 0);
2035 _save_project (GESFormatter * formatter, GString * str, GESProject * project,
2036 GESTimeline * timeline, GError ** error, guint depth)
2038 gchar *projstr = NULL, *version;
2039 gchar *properties = NULL, *metas = NULL;
2040 GESXmlFormatter *self = GES_XML_FORMATTER (formatter);
2041 GESXmlFormatterPrivate *priv = _GET_PRIV (formatter);
2043 properties = _serialize_properties (G_OBJECT (project), NULL, NULL);
2044 metas = ges_meta_container_metas_to_string (GES_META_CONTAINER (project));
2045 append_escaped (str,
2046 g_markup_printf_escaped (" <project properties='%s' metadatas='%s'>\n",
2047 properties, metas), depth);
2048 g_free (properties);
2051 string_append_with_depth (str, " <encoding-profiles>\n", depth);
2052 _save_encoding_profiles (GES_XML_FORMATTER (formatter), str, project, depth);
2053 string_append_with_depth (str, " </encoding-profiles>\n", depth);
2055 string_append_with_depth (str, " <ressources>\n", depth);
2056 if (!_save_assets (self, str, project, error, depth)) {
2057 g_string_free (str, TRUE);
2060 string_append_with_depth (str, " </ressources>\n", depth);
2062 _save_timeline (self, str, timeline, depth);
2063 string_append_with_depth (str, " </project>\n", depth);
2064 string_append_with_depth (str, "</ges>\n", depth);
2066 projstr = g_strdup_printf ("<ges version='%i.%i'>\n", API_VERSION,
2068 g_string_prepend (str, projstr);
2069 string_add_indents (str, depth, TRUE);
2072 ges_meta_container_set_int (GES_META_CONTAINER (project),
2073 GES_META_FORMAT_VERSION, priv->min_version);
2075 version = g_strdup_printf ("%d.%d", API_VERSION,
2076 GES_XML_FORMATTER (formatter)->priv->min_version);
2078 ges_meta_container_set_string (GES_META_CONTAINER (project),
2079 GES_META_FORMAT_VERSION, version);
2089 _setup_subprojects_map (GESXmlFormatterPrivate * priv, const gchar * uri)
2091 GHashTable *subprojects_map;
2093 G_LOCK (uri_subprojects_map_lock);
2094 if (!uri_subprojects_map)
2095 uri_subprojects_map =
2096 g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
2097 (GDestroyNotify) g_hash_table_unref);
2099 subprojects_map = g_hash_table_lookup (uri_subprojects_map, uri);
2100 if (!subprojects_map) {
2102 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
2103 g_hash_table_insert (uri_subprojects_map, g_strdup (uri), subprojects_map);
2105 priv->subprojects_map = subprojects_map;
2106 G_UNLOCK (uri_subprojects_map_lock);
2111 ges_xml_formatter_deinit (void)
2113 GST_DEBUG ("Deinit");
2114 G_LOCK (uri_subprojects_map_lock);
2115 if (uri_subprojects_map) {
2116 g_hash_table_unref (uri_subprojects_map);
2117 uri_subprojects_map = NULL;
2119 G_UNLOCK (uri_subprojects_map_lock);
2123 _save_to_uri (GESFormatter * formatter, GESTimeline * timeline,
2124 const gchar * uri, gboolean overwrite, GError ** error)
2126 _setup_subprojects_map (_GET_PRIV (formatter), uri);
2127 return GES_FORMATTER_CLASS (parent_class)->save_to_uri (formatter, timeline,
2128 uri, overwrite, error);
2132 _can_load_uri (GESFormatter * formatter, const gchar * uri, GError ** error)
2134 _setup_subprojects_map (_GET_PRIV (formatter), uri);
2135 return GES_FORMATTER_CLASS (parent_class)->can_load_uri (formatter, uri,
2140 _load_from_uri (GESFormatter * formatter, GESTimeline * timeline,
2141 const gchar * uri, GError ** error)
2143 _setup_subprojects_map (_GET_PRIV (formatter), uri);
2144 return GES_FORMATTER_CLASS (parent_class)->load_from_uri (formatter, timeline,
2148 /***********************************************
2150 * GObject virtual methods implementation *
2152 ***********************************************/
2155 _get_property (GObject * object, guint property_id,
2156 GValue * value, GParamSpec * pspec)
2161 _set_property (GObject * object, guint property_id,
2162 const GValue * value, GParamSpec * pspec)
2167 ges_xml_formatter_init (GESXmlFormatter * self)
2169 GESXmlFormatterPrivate *priv = ges_xml_formatter_get_instance_private (self);
2171 priv->project_opened = FALSE;
2172 priv->element_id = g_hash_table_new (g_direct_hash, g_direct_equal);
2175 self->priv->min_version = 1;
2179 _dispose (GObject * object)
2181 g_clear_pointer (&GES_XML_FORMATTER (object)->priv->element_id,
2182 g_hash_table_unref);
2184 G_OBJECT_CLASS (parent_class)->dispose (object);
2188 ges_xml_formatter_class_init (GESXmlFormatterClass * self_class)
2190 GObjectClass *object_class = G_OBJECT_CLASS (self_class);
2191 GESBaseXmlFormatterClass *basexmlformatter_class;
2192 GESFormatterClass *formatter_klass = GES_FORMATTER_CLASS (self_class);
2194 basexmlformatter_class = GES_BASE_XML_FORMATTER_CLASS (self_class);
2196 formatter_klass->save_to_uri = _save_to_uri;
2197 formatter_klass->can_load_uri = _can_load_uri;
2198 formatter_klass->load_from_uri = _load_from_uri;
2200 object_class->get_property = _get_property;
2201 object_class->set_property = _set_property;
2202 object_class->dispose = _dispose;
2204 basexmlformatter_class->content_parser.start_element = _parse_element_start;
2205 basexmlformatter_class->content_parser.end_element = _parse_element_end;
2206 basexmlformatter_class->content_parser.text = NULL;
2207 basexmlformatter_class->content_parser.passthrough = NULL;
2208 basexmlformatter_class->content_parser.error = _error_parsing;
2210 ges_formatter_class_register_metas (GES_FORMATTER_CLASS (self_class),
2211 "ges", "GStreamer Editing Services project files",
2212 "xges", "application/xges", VERSION, GST_RANK_PRIMARY);
2214 basexmlformatter_class->save = _save;
2217 #undef COLLECT_STR_OPT