diff --git a/docs/libs/ges-docs.sgml b/docs/libs/ges-docs.sgml index 0ee8e90b61..0e09fd75c4 100644 --- a/docs/libs/ges-docs.sgml +++ b/docs/libs/ges-docs.sgml @@ -89,6 +89,8 @@ platform as well as Windows. It is released under the GNU Library General Public Serialization Classes + + diff --git a/docs/libs/ges-sections.txt b/docs/libs/ges-sections.txt index 68299e2b32..806b640b47 100644 --- a/docs/libs/ges-sections.txt +++ b/docs/libs/ges-sections.txt @@ -1015,4 +1015,31 @@ GES_PROJECT_GET_CLASS GES_TYPE_PROJECT +
+ges-base-xml-formatter +GESBaseXmlFormatter +GESBaseXmlFormatter +ges_base_xml_formatter_get_type + +GESBaseXmlFormatterPrivate +GES_BASE_XML_FORMATTER +GES_BASE_XML_FORMATTER_CLASS +GES_IS_BASE_XML_FORMATTER +GES_IS_BASE_XML_FORMATTER_CLASS +GES_BASE_XML_FORMATTER_GET_CLASS +GES_TYPE_BASE_XML_FORMATTER +
+ +
+ges-xml-formatter +GESXmlFormatter +ges_xml_formatter_get_type + +GESXmlFormatterPrivate +GES_XML_FORMATTER +GES_TYPE_XML_FORMATTER +GES_XML_FORMATTER_CLASS +GES_XML_FORMATTER_GET_CLASS +GES_IS_XML_FORMATTER +GES_IS_XML_FORMATTER_CLASS
diff --git a/ges/Makefile.am b/ges/Makefile.am index 03ba400ca8..6ad6ddb7a9 100644 --- a/ges/Makefile.am +++ b/ges/Makefile.am @@ -51,6 +51,8 @@ libges_@GST_API_VERSION@_la_SOURCES = \ ges-asset-file-source.c \ ges-extractable.c \ ges-project.c \ + ges-base-xml-formatter.c \ + ges-xml-formatter.c \ ges-utils.c libges_@GST_API_VERSION@includedir = $(includedir)/gstreamer-@GST_API_VERSION@/ges/ @@ -99,6 +101,8 @@ libges_@GST_API_VERSION@include_HEADERS = \ ges-asset-file-source.h \ ges-extractable.h \ ges-project.h \ + ges-base-xml-formatter.h \ + ges-xml-formatter.h \ ges-utils.h noinst_HEADERS = \ @@ -153,6 +157,7 @@ GES-@GST_API_VERSION@.gir: $(INTROSPECTION_SCANNER) libges-@GST_API_VERSION@.la --library=libges-@GST_API_VERSION@.la \ --include=Gst-@GST_API_VERSION@ \ --include=GstPbutils-@GST_API_VERSION@ \ + --include=Gio-2.0 \ --libtool="$(top_builddir)/libtool" \ --pkg gstreamer-@GST_API_VERSION@ \ --pkg gstreamer-pbutils-@GST_API_VERSION@ \ diff --git a/ges/ges-base-xml-formatter.c b/ges/ges-base-xml-formatter.c new file mode 100644 index 0000000000..24feaf6587 --- /dev/null +++ b/ges/ges-base-xml-formatter.c @@ -0,0 +1,955 @@ +/* Gstreamer Editing Services + * + * Copyright (C) <2012> Thibault Saunier + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "ges.h" +#include "ges-internal.h" + +/* TODO: + * + Handle Groups + **/ +#define parent_class ges_base_xml_formatter_parent_class +G_DEFINE_ABSTRACT_TYPE (GESBaseXmlFormatter, ges_base_xml_formatter, + GES_TYPE_FORMATTER); + +#define _GET_PRIV(o)\ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), GES_TYPE_BASE_XML_FORMATTER, GESBaseXmlFormatterPrivate)) + +typedef struct PendingEffects +{ + gchar *track_id; + GESTrackObject *tckobj; + GstStructure *children_properties; + GstStructure *properties; + +} PendingEffects; + +typedef struct PendingTimelineObject +{ + gchar *id; + gdouble rate; + guint layer_prio; + GstClockTime start; + GstClockTime inpoint; + GESAsset *asset; + GstClockTime duration; + GESTrackType track_types; + GESTimelineLayer *layer; + + GstStructure *properties; + gchar *metadatas; + + GList *effects; + + /* TODO Implement asset effect management + * PendingTrackObjects *track_objects; */ +} PendingTimelineObject; + +typedef struct LayerEntry +{ + GESTimelineLayer *layer; + gboolean auto_trans; +} LayerEntry; + +typedef struct PendingAsset +{ + GESFormatter *formatter; + gchar *metadatas; + GstStructure *properties; +} PendingAsset; + +struct _GESBaseXmlFormatterPrivate +{ + GMarkupParseContext *parsecontext; + gboolean check_only; + + /* Asset.id -> PendingTimelineObject */ + GHashTable *assetid_pendingtlobjs; + + /* TimelineObject.ID -> Pending */ + GHashTable *tlobjid_pendings; + + /* TimelineObject.ID -> TimelineObject */ + GHashTable *timeline_objects; + + /* ID -> track */ + GHashTable *tracks; + + /* layer.prio -> LayerEntry */ + GHashTable *layers; + + /* List of asset waited to be created */ + GList *pending_assets; +}; + +static void +_free_layer_entry (LayerEntry * entry) +{ + gst_object_unref (entry->layer); + g_slice_free (LayerEntry, entry); +} + +/* +enum +{ + PROP_0, + PROP_LAST +}; +static GParamSpec *properties[PROP_LAST]; + +enum +{ + LAST_SIGNAL +}; +static guint signals[LAST_SIGNAL]; +*/ + +static GMarkupParseContext * +create_parser_context (GESBaseXmlFormatter * self, const gchar * uri, + GError ** error) +{ + GFile *file; + gsize xmlsize; + gchar *xmlcontent; + GMarkupParseContext *parsecontext = NULL; + GESBaseXmlFormatterClass *self_class = + GES_BASE_XML_FORMATTER_GET_CLASS (self); + + GError *err = NULL; + + if ((file = g_file_new_for_uri (uri)) == NULL) + goto wrong_uri; + + /* TODO Handle GCancellable */ + if (!g_file_load_contents (file, NULL, &xmlcontent, &xmlsize, NULL, &err)) + goto failed; + + parsecontext = g_markup_parse_context_new (&self_class->content_parser, + G_MARKUP_TREAT_CDATA_AS_TEXT, self, NULL); + + if (g_markup_parse_context_parse (parsecontext, xmlcontent, xmlsize, + &err) == FALSE) + goto failed; + + return parsecontext; + +wrong_uri: + GST_WARNING ("%s wrong uri", uri); + return NULL; + +failed: + g_propagate_error (error, err); + + if (parsecontext) + g_markup_parse_context_free (parsecontext); + + g_object_unref (file); + + return NULL; +} + +/*********************************************** + * * + * GESFormatter virtual methods implementation * + * * + ***********************************************/ + +static gboolean +_can_load_uri (GESFormatterClass * class, const gchar * uri, GError ** error) +{ + GMarkupParseContext *ctx; + + /* we create a temporary object so we can use it as a context */ + GESBaseXmlFormatter *self = g_object_new (G_OBJECT_CLASS_TYPE (class), NULL); + _GET_PRIV (self)->check_only = TRUE; + + + ctx = create_parser_context (self, uri, error); + if (!ctx) + return FALSE; + + g_markup_parse_context_free (ctx); + + return TRUE; +} + +static gboolean +_load_from_uri (GESFormatter * self, GESTimeline * timeline, const gchar * uri, + GError ** error) +{ + GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self); + + priv->parsecontext = + create_parser_context (GES_BASE_XML_FORMATTER (self), uri, error); + + if (!priv->parsecontext) + return FALSE; + + return TRUE; +} + +static gboolean +_save_to_uri (GESFormatter * formatter, GESTimeline * timeline, + const gchar * uri, gboolean overwrite, GError ** error) +{ + GFile *file; + gboolean ret; + GString *str; + GOutputStream *stream; + GError *lerror = NULL; + + g_return_val_if_fail (formatter->project, FALSE); + + file = g_file_new_for_uri (uri); + stream = G_OUTPUT_STREAM (g_file_create (file, G_FILE_CREATE_NONE, NULL, + &lerror)); + if (stream == NULL) { + if (overwrite && lerror->code == G_IO_ERROR_EXISTS) { + g_clear_error (&lerror); + stream = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE, + G_FILE_CREATE_NONE, NULL, &lerror)); + } + + if (stream == NULL) + goto failed_opening_file; + } + + str = GES_BASE_XML_FORMATTER_GET_CLASS (formatter)->save (formatter, + timeline, error); + + if (str == NULL) + goto serialization_failed; + + ret = g_output_stream_write_all (stream, str->str, str->len, NULL, + NULL, &lerror); + ret = g_output_stream_close (stream, NULL, &lerror); + + if (ret == FALSE) + GST_WARNING_OBJECT (formatter, "Could not save %s because: %s", uri, + lerror->message); + + g_string_free (str, TRUE); + g_object_unref (file); + g_object_unref (stream); + + if (lerror) + g_propagate_error (error, lerror); + + return ret; + +serialization_failed: + g_object_unref (file); + + g_output_stream_close (stream, NULL, NULL); + g_object_unref (stream); + if (lerror) + g_propagate_error (error, lerror); + + return FALSE; + +failed_opening_file: + g_object_unref (file); + + GST_WARNING_OBJECT (formatter, "Could not open %s because: %s", uri, + lerror->message); + + if (lerror) + g_propagate_error (error, lerror); + + return FALSE; +} + +/*********************************************** + * * + * GOBject virtual methods implementation * + * * + ***********************************************/ + +static void +_dispose (GObject * object) +{ + GESBaseXmlFormatterPrivate *priv = _GET_PRIV (object); + + g_clear_pointer (&priv->assetid_pendingtlobjs, + (GDestroyNotify) g_hash_table_unref); + g_clear_pointer (&priv->timeline_objects, + (GDestroyNotify) g_hash_table_unref); + g_clear_pointer (&priv->tlobjid_pendings, + (GDestroyNotify) g_hash_table_unref); + g_clear_pointer (&priv->tracks, (GDestroyNotify) g_hash_table_unref); + g_clear_pointer (&priv->layers, (GDestroyNotify) g_hash_table_unref); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +_finalize (GObject * object) +{ + GESBaseXmlFormatterPrivate *priv = _GET_PRIV (object); + + if (priv->parsecontext != NULL) + g_markup_parse_context_free (priv->parsecontext); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +ges_base_xml_formatter_init (GESBaseXmlFormatter * self) +{ + GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self); + + priv->check_only = FALSE; + priv->parsecontext = NULL; + priv->pending_assets = NULL; + + /* The PendingTimelineObject are owned by the assetid_pendingtlobjs table */ + priv->assetid_pendingtlobjs = g_hash_table_new_full (g_str_hash, + g_str_equal, g_free, NULL); + priv->tlobjid_pendings = g_hash_table_new_full (g_str_hash, + g_str_equal, g_free, NULL); + priv->timeline_objects = g_hash_table_new_full (g_str_hash, + g_str_equal, g_free, gst_object_unref); + priv->tracks = g_hash_table_new_full (g_str_hash, + g_str_equal, g_free, gst_object_unref); + priv->layers = g_hash_table_new_full (g_direct_hash, + g_direct_equal, NULL, (GDestroyNotify) _free_layer_entry); +} + +static void +ges_base_xml_formatter_class_init (GESBaseXmlFormatterClass * self_class) +{ + GESFormatterClass *formatter_klass = GES_FORMATTER_CLASS (self_class); + GObjectClass *object_class = G_OBJECT_CLASS (self_class); + + g_type_class_add_private (self_class, sizeof (GESBaseXmlFormatterPrivate)); + object_class->dispose = _dispose; + object_class->finalize = _finalize; + + formatter_klass->can_load_uri = _can_load_uri; + formatter_klass->load_from_uri = _load_from_uri; + formatter_klass->save_to_uri = _save_to_uri; + + self_class->save = NULL; +} + +/*********************************************** + * * + * Private methods * + * * + ***********************************************/ + +static void +_set_auto_transition (gpointer prio, LayerEntry * entry, gpointer udata) +{ + ges_timeline_layer_set_auto_transition (entry->layer, entry->auto_trans); +} + +static void +_loading_done (GESFormatter * self) +{ + GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self); + + if (priv->parsecontext) + g_markup_parse_context_free (priv->parsecontext); + priv->parsecontext = NULL; + + g_hash_table_foreach (priv->layers, (GHFunc) _set_auto_transition, NULL); + ges_project_set_loaded (self->project, self); +} + +static void +_set_child_property (GQuark field_id, const GValue * value, + GESTrackObject * effect) +{ + GParamSpec *pspec; + GstElement *element; + + if (!ges_track_object_lookup_child (effect, + g_quark_to_string (field_id), &element, &pspec)) + return; + + g_object_set_property (G_OBJECT (element), pspec->name, value); + g_param_spec_unref (pspec); + g_object_unref (element); +} + +void +set_property_foreach (GQuark field_id, const GValue * value, GObject * object) +{ + g_object_set_property (object, g_quark_to_string (field_id), value); +} + +static inline GESTimelineObject * +_add_object_to_layer (GESBaseXmlFormatterPrivate * priv, const gchar * id, + GESTimelineLayer * layer, GESAsset * asset, GstClockTime start, + GstClockTime inpoint, GstClockTime duration, gdouble rate, + GESTrackType track_types, const gchar * metadatas, + GstStructure * properties) +{ + GESTimelineObject *tlobj = ges_timeline_layer_add_asset (layer, + asset, start, inpoint, duration, rate, track_types); + + if (tlobj == NULL) { + GST_WARNING_OBJECT ("Could not add object from asset: %s", + ges_asset_get_id (asset)); + + return NULL; + } + + if (metadatas) + ges_meta_container_add_metas_from_string (GES_META_CONTAINER (tlobj), + metadatas); + + if (properties) + gst_structure_foreach (properties, + (GstStructureForeachFunc) set_property_foreach, tlobj); + + g_hash_table_insert (priv->timeline_objects, g_strdup (id), + gst_object_ref (tlobj)); + return tlobj; +} + +static void +_add_track_object (GESFormatter * self, GESTimelineObject * tlobj, + GESTrackObject * tckobj, const gchar * track_id, + GstStructure * children_properties, GstStructure * properties) +{ + GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self); + GESTrack *track = g_hash_table_lookup (priv->tracks, track_id); + + if (track == NULL) { + GST_WARNING_OBJECT (self, "No track with id %s, can not add tckobj", + track_id); + gst_object_unref (tckobj); + return; + } + + GST_DEBUG_OBJECT (self, "Adding track_object: %" GST_PTR_FORMAT + " To : %" GST_PTR_FORMAT, tckobj, tlobj); + + ges_timeline_object_add_track_object (tlobj, tckobj); + ges_track_add_object (track, tckobj); + gst_structure_foreach (children_properties, + (GstStructureForeachFunc) _set_child_property, tckobj); +} + +static void +_free_pending_effect (PendingEffects * pend) +{ + g_free (pend->track_id); + gst_object_unref (pend->tckobj); + if (pend->children_properties) + gst_structure_free (pend->children_properties); + if (pend->properties) + gst_structure_free (pend->properties); + + g_slice_free (PendingEffects, pend); +} + +static void +_free_pending_timeline_object (GESBaseXmlFormatterPrivate * priv, + PendingTimelineObject * pend) +{ + g_free (pend->id); + gst_object_unref (pend->layer); + if (pend->properties) + gst_structure_free (pend->properties); + g_list_free_full (pend->effects, (GDestroyNotify) _free_pending_effect); + g_hash_table_remove (priv->tlobjid_pendings, pend->id); + g_slice_free (PendingTimelineObject, pend); +} + +static void +_free_pending_asset (GESBaseXmlFormatterPrivate * priv, PendingAsset * passet) +{ + if (passet->metadatas) + g_free (passet->metadatas); + if (passet->properties) + gst_structure_free (passet->properties); + g_slice_free (PendingAsset, passet); + + priv->pending_assets = g_list_remove (priv->pending_assets, passet); +} + +static void +new_asset_cb (GESAsset * source, GAsyncResult * res, PendingAsset * passet) +{ + GError *error = NULL; + gchar *possible_id = NULL; + GList *tmp, *pendings = NULL; + GESFormatter *self = passet->formatter; + const gchar *id = ges_asset_get_id (source); + GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self); + GESAsset *asset = ges_asset_request_finish (res, &error); + + if (error) { + GST_LOG_OBJECT (self, "Error %s creating asset id: %s", error->message, id); + + /* We set the metas on the Asset to give hints to the user */ + if (passet->metadatas) + ges_meta_container_add_metas_from_string (GES_META_CONTAINER (source), + passet->metadatas); + if (passet->properties) + gst_structure_foreach (passet->properties, + (GstStructureForeachFunc) set_property_foreach, source); + + possible_id = ges_project_try_updating_id (GES_FORMATTER (self)->project, + source, error); + + if (possible_id == NULL) { + GST_WARNING_OBJECT (self, "Abandoning creation of asset %s with ID %s" + "- Error: %s", g_type_name (G_OBJECT_TYPE (source)), id, + error->message); + + pendings = g_hash_table_lookup (priv->assetid_pendingtlobjs, id); + for (tmp = pendings; tmp; tmp = tmp->next) + _free_pending_timeline_object (priv, + (PendingTimelineObject *) tmp->data); + + _free_pending_asset (priv, passet); + goto done; + } + + /* We got a possible ID replacement for that asset, create it, and + * make sure the assetid_pendingtlobjs will use it */ + ges_asset_request_async (ges_asset_get_extractable_type (source), + possible_id, NULL, (GAsyncReadyCallback) new_asset_cb, passet); + + pendings = g_hash_table_lookup (priv->assetid_pendingtlobjs, id); + if (pendings) { + g_hash_table_remove (priv->assetid_pendingtlobjs, id); + g_hash_table_insert (priv->assetid_pendingtlobjs, + g_strdup (possible_id), pendings); + + /* pendings should no be freed */ + pendings = NULL; + } + goto done; + } + + /* now that we have the GESAsset, we create the GESTimelineObjects */ + pendings = g_hash_table_lookup (priv->assetid_pendingtlobjs, id); + GST_DEBUG_OBJECT (self, "Asset created with ID %s, now creating pending " + " TimelineObjects, nb pendings: %i", id, g_list_length (pendings)); + for (tmp = pendings; tmp; tmp = tmp->next) { + GList *tmpeffect; + GESTimelineObject *tlobj; + PendingTimelineObject *pend = (PendingTimelineObject *) tmp->data; + + tlobj = + _add_object_to_layer (priv, pend->id, pend->layer, asset, + pend->start, pend->inpoint, pend->duration, pend->rate, + pend->track_types, pend->metadatas, pend->properties); + + if (tlobj == NULL) + continue; + + GST_DEBUG_OBJECT (self, "Adding %i effect to new object", + g_list_length (pend->effects)); + for (tmpeffect = pend->effects; tmpeffect; tmpeffect = tmpeffect->next) { + PendingEffects *peffect = (PendingEffects *) tmpeffect->data; + + /* We keep a ref as _free_pending_effect unrefs it */ + _add_track_object (self, tlobj, gst_object_ref (peffect->tckobj), + peffect->track_id, peffect->children_properties, peffect->properties); + } + _free_pending_timeline_object (priv, pend); + } + + /* And now add to the project */ + ges_project_add_asset (self->project, asset); + + gst_object_unref (self); + + _free_pending_asset (priv, passet); + +done: + if (asset) + gst_object_unref (asset); + if (possible_id) + g_free (possible_id); + + if (pendings) { + g_hash_table_remove (priv->assetid_pendingtlobjs, id); + g_list_free (pendings); + } + + if (g_hash_table_size (priv->assetid_pendingtlobjs) == 0 && + priv->pending_assets == NULL) + _loading_done (self); +} + +static GstEncodingProfile * +_create_profile (GESBaseXmlFormatter * self, + const gchar * type, const gchar * parent, const gchar * name, + const gchar * description, GstCaps * format, const gchar * preset, + const gchar * preset_name, gint id, guint presence, GstCaps * restriction, + guint pass, gboolean variableframerate) +{ + GstEncodingProfile *profile = NULL; + + if (!g_strcmp0 (type, "container")) { + profile = GST_ENCODING_PROFILE (gst_encoding_container_profile_new (name, + description, format, preset)); + gst_encoding_profile_set_preset_name (profile, preset_name); + + return profile; + } else if (!g_strcmp0 (type, "video")) { + GstEncodingVideoProfile *sprof = gst_encoding_video_profile_new (format, + preset, restriction, presence); + + gst_encoding_video_profile_set_variableframerate (sprof, variableframerate); + gst_encoding_video_profile_set_pass (sprof, pass); + + profile = GST_ENCODING_PROFILE (sprof); + } else if (!g_strcmp0 (type, "audio")) { + profile = GST_ENCODING_PROFILE (gst_encoding_audio_profile_new (format, + preset, restriction, presence)); + } else { + GST_ERROR_OBJECT (self, "Unknown profile format '%s'", type); + + return NULL; + } + + gst_encoding_profile_set_name (profile, name); + gst_encoding_profile_set_description (profile, description); + gst_encoding_profile_set_preset_name (profile, preset_name); + + return profile; +} + +/*********************************************** + * * + * Public methods * + * * + ***********************************************/ + +void +ges_base_xml_formatter_add_asset (GESBaseXmlFormatter * self, + const gchar * id, GType extractable_type, GstStructure * properties, + const gchar * metadatas, GError ** error) +{ + PendingAsset *passet; + GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self); + + if (priv->check_only) + return; + + passet = g_slice_new0 (PendingAsset); + passet->metadatas = g_strdup (metadatas); + passet->formatter = gst_object_ref (self); + if (properties) + passet->properties = gst_structure_copy (properties); + ges_asset_request_async (extractable_type, id, NULL, + (GAsyncReadyCallback) new_asset_cb, passet); + priv->pending_assets = g_list_prepend (priv->pending_assets, passet); +} + +void +ges_base_xml_formatter_add_timeline_object (GESBaseXmlFormatter * self, + const gchar * id, const char *asset_id, GType type, GstClockTime start, + GstClockTime inpoint, GstClockTime duration, gdouble rate, + guint layer_prio, GESTrackType track_types, GstStructure * properties, + const gchar * metadatas, GError ** error) +{ + GESAsset *asset; + GESTimelineObject *nobj; + LayerEntry *entry; + GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self); + + if (priv->check_only) + return; + + entry = g_hash_table_lookup (priv->layers, GINT_TO_POINTER (layer_prio)); + if (entry == NULL) { + g_set_error (error, GES_ERROR_DOMAIN, 0, + "We got a TimelineObject in a layer" + " that does not exist, something is wrong either in the project file or" + " in %s", g_type_name (G_OBJECT_TYPE (self))); + } + + /* We do not want the properties that are passed to layer-add_asset to be reset */ + if (properties) + gst_structure_remove_fields (properties, "supported-formats", "rate", + "inpoint", "start", "ducation", NULL); + + asset = ges_asset_request (type, asset_id, NULL); + if (asset == NULL) { + gchar *real_id; + PendingTimelineObject *ptlobj; + GList *pendings; + + real_id = ges_extractable_type_check_id (type, asset_id, error); + if (real_id == NULL) { + if (*error == NULL) + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "Object type '%s' with Asset id: %s not be created'", + g_type_name (type), asset_id); + + return; + } + + pendings = g_hash_table_lookup (priv->assetid_pendingtlobjs, asset_id); + + ptlobj = g_slice_new0 (PendingTimelineObject); + GST_DEBUG_OBJECT (self, "Adding pending %p for %s, currently: %i", + ptlobj, asset_id, g_list_length (pendings)); + + ptlobj->id = g_strdup (id); + ptlobj->rate = rate; + ptlobj->track_types = track_types; + ptlobj->duration = duration; + ptlobj->inpoint = inpoint; + ptlobj->start = start; + ptlobj->layer = gst_object_ref (entry->layer); + + ptlobj->properties = properties ? gst_structure_copy (properties) : NULL; + ptlobj->metadatas = g_strdup (metadatas); + + /* Add the new pending object to the hashtable */ + g_hash_table_insert (priv->assetid_pendingtlobjs, real_id, + g_list_append (pendings, ptlobj)); + g_hash_table_insert (priv->tlobjid_pendings, g_strdup (id), ptlobj); + + return; + } + + nobj = _add_object_to_layer (priv, id, entry->layer, + asset, start, inpoint, duration, rate, track_types, metadatas, + properties); + + if (!nobj) + return; +} + +void +ges_base_xml_formatter_add_layer (GESBaseXmlFormatter * self, + GType extractable_type, guint priority, GstStructure * properties, + const gchar * metadatas, GError ** error) +{ + LayerEntry *entry; + GESAsset *asset; + GESTimelineLayer *layer; + gboolean auto_transition = FALSE; + GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self); + + if (priv->check_only) + return; + + if (extractable_type == G_TYPE_NONE) + layer = ges_timeline_layer_new (); + else { + asset = ges_asset_request (extractable_type, NULL, error); + if (asset == NULL) { + if (error && *error == NULL) { + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "Layer type %s could not be created'", + g_type_name (extractable_type)); + return; + } + } + layer = GES_TIMELINE_LAYER (ges_asset_extract (asset, error)); + } + + ges_timeline_layer_set_priority (layer, priority); + ges_timeline_add_layer (GES_FORMATTER (self)->timeline, layer); + if (properties) { + if (gst_structure_get_boolean (properties, "auto-transition", + &auto_transition)) + gst_structure_remove_field (properties, "auto-transition"); + + gst_structure_foreach (properties, + (GstStructureForeachFunc) set_property_foreach, layer); + } + + if (metadatas) + ges_meta_container_add_metas_from_string (GES_META_CONTAINER (layer), + metadatas); + + entry = g_slice_new0 (LayerEntry); + entry->layer = gst_object_ref (layer); + entry->auto_trans = auto_transition; + + g_hash_table_insert (priv->layers, GINT_TO_POINTER (priority), entry); +} + +void +ges_base_xml_formatter_add_track (GESBaseXmlFormatter * self, + GESTrackType track_type, GstCaps * caps, const gchar * id, + GstStructure * properties, const gchar * metadatas, GError ** error) +{ + GESTrack *track; + GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self); + + if (priv->check_only) + return; + + track = ges_track_new (track_type, caps); + ges_timeline_add_track (GES_FORMATTER (self)->timeline, track); + if (properties) + gst_structure_foreach (properties, + (GstStructureForeachFunc) set_property_foreach, track); + + g_hash_table_insert (priv->tracks, g_strdup (id), gst_object_ref (track)); + if (metadatas) + ges_meta_container_add_metas_from_string (GES_META_CONTAINER (track), + metadatas); +} + +void +ges_base_xml_formatter_add_track_object (GESBaseXmlFormatter * self, + GType track_object_type, const gchar * asset_id, const gchar * track_id, + const gchar * timeline_obj_id, GstStructure * children_properties, + GstStructure * properties, const gchar * metadatas, GError ** error) +{ + GESTrackObject *tckobj; + + GError *err = NULL; + GESAsset *asset = NULL; + GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self); + + if (priv->check_only) + return; + + if (g_type_is_a (track_object_type, GES_TYPE_TRACK_OBJECT) == FALSE) { + GST_DEBUG_OBJECT (self, "%s is not a TrackObject, can not create it", + g_type_name (track_object_type)); + goto out; + } + + if (g_type_is_a (track_object_type, GES_TYPE_TRACK_EFFECT) == FALSE) { + GST_FIXME_OBJECT (self, "%s currently not supported", + g_type_name (track_object_type)); + goto out; + } + + asset = ges_asset_request (track_object_type, asset_id, &err); + if (asset == NULL) { + GST_DEBUG_OBJECT (self, "Can not create tckobj %s", asset_id); + GST_FIXME_OBJECT (self, "Check if missing plugins etc %s", + err ? err->message : ""); + + goto out; + } + + tckobj = GES_TRACK_OBJECT (ges_asset_extract (asset, NULL)); + if (tckobj) { + GESTimelineObject *tlobj; + if (metadatas) + ges_meta_container_add_metas_from_string (GES_META_CONTAINER (tckobj), + metadatas); + + tlobj = g_hash_table_lookup (priv->timeline_objects, timeline_obj_id); + if (tlobj) { + _add_track_object (GES_FORMATTER (self), tlobj, tckobj, track_id, + children_properties, properties); + } else { + PendingEffects *peffect; + PendingTimelineObject *pend = g_hash_table_lookup (priv->tlobjid_pendings, + timeline_obj_id); + if (pend == NULL) { + GST_WARNING_OBJECT (self, "No TimelineObject with id: %s can not " + "add TrackObject", timeline_obj_id); + goto out; + } + + peffect = g_slice_new0 (PendingEffects); + + peffect->tckobj = tckobj; + peffect->track_id = g_strdup (track_id); + peffect->properties = properties ? gst_structure_copy (properties) : NULL; + peffect->children_properties = children_properties ? + gst_structure_copy (children_properties) : NULL; + + pend->effects = g_list_append (pend->effects, peffect); + } + } + + ges_project_add_asset (GES_FORMATTER (self)->project, asset); + +out: + if (asset) + gst_object_unref (asset); + if (err) + g_error_free (err); + + return; +} + +void +ges_base_xml_formatter_add_encoding_profile (GESBaseXmlFormatter * self, + const gchar * type, const gchar * parent, const gchar * name, + const gchar * description, GstCaps * format, const gchar * preset, + const gchar * preset_name, guint id, guint presence, GstCaps * restriction, + guint pass, gboolean variableframerate, GstStructure * properties, + GError ** error) +{ + const GList *tmp; + GstEncodingProfile *profile; + GstEncodingContainerProfile *parent_profile = NULL; + GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self); + + if (priv->check_only) + return; + + if (parent == NULL) { + profile = + _create_profile (self, type, parent, name, description, format, preset, + preset_name, id, presence, restriction, pass, variableframerate); + ges_project_add_encoding_profile (GES_FORMATTER (self)->project, profile); + gst_object_unref (profile); + + return; + } + + for (tmp = ges_project_list_encoding_profiles (GES_FORMATTER (self)->project); + tmp; tmp = tmp->next) { + GstEncodingProfile *tmpprofile = GST_ENCODING_PROFILE (tmp->data); + + if (g_strcmp0 (gst_encoding_profile_get_name (tmpprofile), + gst_encoding_profile_get_name (tmpprofile)) == 0) { + + if (!GST_IS_ENCODING_CONTAINER_PROFILE (tmpprofile)) { + g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, + "Profile '%s' parent %s is not a container...'", name, parent); + return; + } + + parent_profile = GST_ENCODING_CONTAINER_PROFILE (tmpprofile); + break; + } + } + + if (parent_profile == NULL) { + g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, + "Profile '%s' parent %s does not exist'", name, parent); + return; + } + + profile = + _create_profile (self, type, parent, name, description, format, preset, + preset_name, id, presence, restriction, pass, variableframerate); + + if (profile == NULL) + return; + + gst_encoding_container_profile_add_profile (parent_profile, profile); +} diff --git a/ges/ges-base-xml-formatter.h b/ges/ges-base-xml-formatter.h new file mode 100644 index 0000000000..9448580e07 --- /dev/null +++ b/ges/ges-base-xml-formatter.h @@ -0,0 +1,58 @@ +/* Gstreamer Editing Services + * + * Copyright (C) <2012> Thibault Saunier + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "ges-formatter.h" + +#ifndef GES_BASE_XML_FORMATTER_H +#define GES_BASE_XML_FORMATTER_H + +G_BEGIN_DECLS +#define GES_TYPE_BASE_XML_FORMATTER (ges_base_xml_formatter_get_type ()) +#define GES_BASE_XML_FORMATTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_BASE_XML_FORMATTER, GESBaseXmlFormatter)) +#define GES_BASE_XML_FORMATTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_BASE_XML_FORMATTER, GESBaseXmlFormatterClass)) +#define GES_IS_BASE_XML_FORMATTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_BASE_XML_FORMATTER)) +#define GES_IS_BASE_XML_FORMATTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_BASE_XML_FORMATTER)) +#define GES_BASE_XML_FORMATTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_BASE_XML_FORMATTER, GESBaseXmlFormatterClass)) +typedef struct _GESBaseXmlFormatterPrivate GESBaseXmlFormatterPrivate; + +typedef struct +{ + GESFormatter parent; + + /* */ + GESBaseXmlFormatterPrivate *priv; + +} GESBaseXmlFormatter; + +typedef struct +{ + GESFormatterClass parent; + + /* Should be overriden by subclasses */ + GMarkupParser content_parser; + + GString * (*save) (GESFormatter *formatter, GESTimeline *timeline, GError **error); + +} GESBaseXmlFormatterClass; + +GType ges_base_xml_formatter_get_type (void); + +G_END_DECLS +#endif /* _GES_BASE_XML_FORMATTER_H */ diff --git a/ges/ges-internal.h b/ges/ges-internal.h index cdc570096c..425417b3b2 100644 --- a/ges/ges-internal.h +++ b/ges/ges-internal.h @@ -28,6 +28,7 @@ #include "ges-track-object.h" #include "ges-asset.h" +#include "ges-base-xml-formatter.h" GST_DEBUG_CATEGORY_EXTERN (_ges_debug); #define GST_CAT_DEFAULT _ges_debug @@ -103,7 +104,11 @@ ges_extractable_get_real_extractable_type_for_id (GType type, const gchar * id); gboolean ges_extractable_register_metas (GType extractable_type, GESAsset *asset); -/* GESFormatter internal methods */ +/************************************************ + * * + * GESFormatter internal methods * + * * + ************************************************/ void ges_formatter_set_project (GESFormatter *formatter, GESProject *project); @@ -111,12 +116,92 @@ GESProject * ges_formatter_get_project (GESFormatter *formatter); GESAsset * _find_formatter_asset_for_uri (const gchar *uri); -/* GESProject internal methods */ + + +/************************************************ + * * + * GESProject internal methods * + * * + ************************************************/ + +/* FIXME This should probably become public, but we need to make sure it + * is the right API before doing so*/ gboolean ges_project_set_loaded (GESProject * project, GESFormatter *formatter); gchar * ges_project_try_updating_id (GESProject *self, GESAsset *asset, GError *error); -void _init_formatter_assets (void); -#endif +/************************************************ + * * + * GESBaseXmlFormatter internal methods * + * * + ************************************************/ + +/* FIXME GESBaseXmlFormatter is all internal for now, the API is not stable + * fo now, so do not expose it */ +G_GNUC_INTERNAL void ges_base_xml_formatter_add_timeline_object (GESBaseXmlFormatter * self, + const gchar *id, + const char *asset_id, + GType type, + GstClockTime start, + GstClockTime inpoint, + GstClockTime duration, + gdouble rate, + guint layer_prio, + GESTrackType track_types, + GstStructure *properties, + const gchar *metadatas, + GError **error); +G_GNUC_INTERNAL void ges_base_xml_formatter_add_asset (GESBaseXmlFormatter * self, + const gchar * id, + GType extractable_type, + GstStructure *properties, + const gchar *metadatas, + GError **error); +G_GNUC_INTERNAL void ges_base_xml_formatter_add_layer (GESBaseXmlFormatter *self, + GType extractable_type, + guint priority, + GstStructure *properties, + const gchar *metadatas, + GError **error); +G_GNUC_INTERNAL void ges_base_xml_formatter_add_track (GESBaseXmlFormatter *self, + GESTrackType track_type, + GstCaps *caps, + const gchar *id, + GstStructure *properties, + const gchar *metadatas, + GError **error); +void ges_base_xml_formatter_add_encoding_profile (GESBaseXmlFormatter * self, + const gchar *type, + const gchar *parent, + const gchar * name, + const gchar * description, + GstCaps * format, + const gchar * preset, + const gchar * preset_name, + guint id, + guint presence, + GstCaps * restriction, + guint pass, + gboolean variableframerate, + GstStructure * properties, + GError ** error); +G_GNUC_INTERNAL void ges_base_xml_formatter_add_track_object (GESBaseXmlFormatter *self, + GType effect_type, + const gchar *asset_id, + const gchar * track_id, + const gchar *timeline_obj_id, + GstStructure *children_properties, + GstStructure *properties, + const gchar *metadatas, + GError **error); +G_GNUC_INTERNAL void set_property_foreach (GQuark field_id, + const GValue * value, + GObject * object);; + +/* Function to initialise GES */ +G_GNUC_INTERNAL void _init_standard_transition_assets (void); +G_GNUC_INTERNAL void _init_formatter_assets (void); + +#endif /* __GES_INTERNAL_H__ */ diff --git a/ges/ges-xml-formatter.c b/ges/ges-xml-formatter.c new file mode 100644 index 0000000000..74131f2ad7 --- /dev/null +++ b/ges/ges-xml-formatter.c @@ -0,0 +1,1110 @@ +/* Gstreamer Editing Services + * + * Copyright (C) <2012> Thibault Saunier + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +/* TODO Determine error codes numbers */ + +#include "ges.h" +#include +#include "ges-internal.h" + +#define parent_class ges_xml_formatter_parent_class +G_DEFINE_TYPE (GESXmlFormatter, ges_xml_formatter, GES_TYPE_BASE_XML_FORMATTER); + +#define API_VERSION 0 +#define MINOR_VERSION 1 +#define VERSION 0.1 + +#define COLLECT_STR_OPT (G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL) + +#define _GET_PRIV(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GES_TYPE_XML_FORMATTER, GESXmlFormatterPrivate)) + +struct _GESXmlFormatterPrivate +{ + gboolean ges_opened; + gboolean project_opened; + + GString *str; +}; + +static inline void +_parse_ges_element (GMarkupParseContext * context, const gchar * element_name, + const gchar ** attribute_names, const gchar ** attribute_values, + GESXmlFormatter * self, GError ** error) +{ + const gchar *version, *properties; + guint api_version, min_version; + + gchar **split_version = NULL; + + if (g_strcmp0 (element_name, "ges")) { + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "element '%s', Missing element'", element_name); + return; + } + + if (!g_markup_collect_attributes (element_name, attribute_names, + attribute_values, error, G_MARKUP_COLLECT_STRING, "version", &version, + COLLECT_STR_OPT, "properties", &properties, + G_MARKUP_COLLECT_INVALID)) { + return; + } + + split_version = g_strsplit (version, ".", 2); + if (split_version[1] == NULL) + goto failed; + + errno = 0; + api_version = g_ascii_strtoull (split_version[0], NULL, 10); + if (errno || api_version != API_VERSION) + goto stroull_failed; + + min_version = g_ascii_strtoull (split_version[1], NULL, 10); + if (min_version > MINOR_VERSION) + goto failed; + + _GET_PRIV (self)->ges_opened = TRUE; + g_strfreev (split_version); + return; + +failed: + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "element '%s', %s wrong version'", element_name, version); + if (split_version) + g_strfreev (split_version); + + return; + +stroull_failed: + GST_WARNING_OBJECT (self, "Error while strtoull: %s", g_strerror (errno)); + goto failed; +} + +static inline void +_parse_project (GMarkupParseContext * context, const gchar * element_name, + const gchar ** attribute_names, const gchar ** attribute_values, + GESXmlFormatter * self, GError ** error) +{ + const gchar *metadatas = NULL, *properties; + GESXmlFormatterPrivate *priv = _GET_PRIV (self); + + if (g_strcmp0 (element_name, "project")) { + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "element '%s', Missing project element'", element_name); + } else { + priv->project_opened = TRUE; + if (!g_markup_collect_attributes (element_name, attribute_names, + attribute_values, error, + COLLECT_STR_OPT, "properties", &properties, + COLLECT_STR_OPT, "metadatas", &metadatas, G_MARKUP_COLLECT_INVALID)) + return; + + if (GES_FORMATTER (self)->project && metadatas) + ges_meta_container_add_metas_from_string (GES_META_CONTAINER + (GES_FORMATTER (self)->project), metadatas); + + } +} + +static inline void +_parse_encoding_profile (GMarkupParseContext * context, + const gchar * element_name, const gchar ** attribute_names, + const gchar ** attribute_values, GESXmlFormatter * self, GError ** error) +{ + GstCaps *capsformat = NULL; + const gchar *name, *description, *type, *preset = NULL, *preset_name = + NULL, *format; + + if (!g_markup_collect_attributes (element_name, attribute_names, + attribute_values, error, + G_MARKUP_COLLECT_STRING, "name", &name, + G_MARKUP_COLLECT_STRING, "description", &description, + G_MARKUP_COLLECT_STRING, "type", &type, + COLLECT_STR_OPT, "preset", &preset, + COLLECT_STR_OPT, "preset-name", &preset_name, + COLLECT_STR_OPT, "format", &format, G_MARKUP_COLLECT_INVALID)) + return; + + if (format) + capsformat = gst_caps_from_string (format); + + ges_base_xml_formatter_add_encoding_profile (GES_BASE_XML_FORMATTER (self), + type, NULL, name, description, capsformat, preset, preset_name, 0, 0, + NULL, 0, FALSE, NULL, error); +} + +static inline void +_parse_stream_profile (GMarkupParseContext * context, + const gchar * element_name, const gchar ** attribute_names, + const gchar ** attribute_values, GESXmlFormatter * self, GError ** error) +{ + gboolean variableframerate = FALSE; + guint id = 0, presence = 0, pass = 0; + GstCaps *format_caps = NULL, *restriction_caps = NULL; + const gchar *parent, *strid, *type, *strpresence, *format = NULL, + *name = NULL, *description = NULL, *preset, *preset_name = + NULL, *restriction = NULL, *strpass = NULL, *strvariableframerate = NULL; + + /* FIXME Looks like there is a bug in that function, if we put the parent + * at the beginning it set %NULL and not the real value... :/ */ + if (!g_markup_collect_attributes (element_name, attribute_names, + attribute_values, error, + G_MARKUP_COLLECT_STRING, "id", &strid, + G_MARKUP_COLLECT_STRING, "type", &type, + G_MARKUP_COLLECT_STRING, "presence", &strpresence, + COLLECT_STR_OPT, "format", &format, + COLLECT_STR_OPT, "name", &name, + COLLECT_STR_OPT, "description", &description, + COLLECT_STR_OPT, "preset", &preset, + COLLECT_STR_OPT, "preset-name", &preset_name, + COLLECT_STR_OPT, "restriction", &restriction, + COLLECT_STR_OPT, "pass", &pass, + COLLECT_STR_OPT, "variableframerate", &strvariableframerate, + G_MARKUP_COLLECT_STRING, "parent", &parent, G_MARKUP_COLLECT_INVALID)) + return; + + errno = 0; + id = g_ascii_strtoll (strid, NULL, 10); + if (errno) + goto convertion_failed; + + if (strpresence) { + presence = g_ascii_strtoll (strpresence, NULL, 10); + if (errno) + goto convertion_failed; + } + + if (strpass) { + pass = g_ascii_strtoll (strpass, NULL, 10); + if (errno) + goto convertion_failed; + } + + if (strvariableframerate) { + variableframerate = g_ascii_strtoll (strvariableframerate, NULL, 10); + if (errno) + goto convertion_failed; + } + + if (format) + format_caps = gst_caps_from_string (format); + + if (restriction) + restriction_caps = gst_caps_from_string (restriction); + + ges_base_xml_formatter_add_encoding_profile (GES_BASE_XML_FORMATTER (self), + type, parent, name, description, format_caps, preset, preset_name, id, + presence, restriction_caps, pass, variableframerate, NULL, error); + + if (format_caps) + gst_caps_unref (format_caps); + if (restriction_caps) + gst_caps_unref (restriction_caps); + + return; + +convertion_failed: + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "element '%s', Wrong property type, error: %s'", element_name, + g_strerror (errno)); + return; +} + +static inline void +_parse_timeline (GMarkupParseContext * context, const gchar * element_name, + const gchar ** attribute_names, const gchar ** attribute_values, + GESXmlFormatter * self, GError ** error) +{ + const gchar *metadatas = NULL, *properties = NULL; + GESTimeline *timeline = GES_FORMATTER (self)->timeline; + + if (!g_markup_collect_attributes (element_name, attribute_names, + attribute_values, error, + COLLECT_STR_OPT, "properties", &properties, + COLLECT_STR_OPT, "metadatas", &metadatas, G_MARKUP_COLLECT_INVALID)) + return; + + if (timeline == NULL) + return; + + if (properties) { + GstStructure *props = gst_structure_from_string (properties, NULL); + + if (props) { + gst_structure_foreach (props, + (GstStructureForeachFunc) set_property_foreach, timeline); + gst_structure_free (props); + } + } + if (metadatas) { + ges_meta_container_add_metas_from_string (GES_META_CONTAINER (timeline), + metadatas); + }; +} + +static inline void +_parse_asset (GMarkupParseContext * context, const gchar * element_name, + const gchar ** attribute_names, const gchar ** attribute_values, + GESXmlFormatter * self, GError ** error) +{ + GType extractable_type; + const gchar *id, *extractable_type_name, *metadatas = NULL, *properties = + NULL; + + if (!g_markup_collect_attributes (element_name, attribute_names, + attribute_values, error, G_MARKUP_COLLECT_STRING, "id", &id, + G_MARKUP_COLLECT_STRING, "extractable-type-name", + &extractable_type_name, + COLLECT_STR_OPT, "properties", &properties, + COLLECT_STR_OPT, "metadatas", &metadatas, G_MARKUP_COLLECT_INVALID)) + return; + + extractable_type = g_type_from_name (extractable_type_name); + if (extractable_type == G_TYPE_NONE) + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "element '%s' invalid extractable_type %s'", + element_name, extractable_type_name); + else if (!g_type_is_a (extractable_type, GES_TYPE_EXTRACTABLE)) + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "element '%s', %s not an extractable_type'", + element_name, extractable_type_name); + else { + GstStructure *props = NULL; + if (properties) + props = gst_structure_from_string (properties, NULL); + + ges_base_xml_formatter_add_asset (GES_BASE_XML_FORMATTER (self), id, + extractable_type, props, metadatas, error); + if (props) + gst_structure_free (props); + } +} + + +static inline void +_parse_track (GMarkupParseContext * context, const gchar * element_name, + const gchar ** attribute_names, const gchar ** attribute_values, + GESXmlFormatter * self, GError ** error) +{ + GstCaps *caps; + GESTrackType track_type; + + const gchar *strtrack_type, *strcaps, *strtrack_id, *metadatas = + NULL, *properties = NULL; + + if (!g_markup_collect_attributes (element_name, attribute_names, + attribute_values, error, + G_MARKUP_COLLECT_STRING, "track-type", &strtrack_type, + G_MARKUP_COLLECT_STRING, "track-id", &strtrack_id, + COLLECT_STR_OPT, "properties", &properties, + COLLECT_STR_OPT, "metadatas", &metadatas, + G_MARKUP_COLLECT_STRING, "caps", &strcaps, G_MARKUP_COLLECT_INVALID)) + return; + + if ((caps = gst_caps_from_string (strcaps)) == NULL) + goto wrong_caps; + + errno = 0; + track_type = g_ascii_strtoll (strtrack_type, NULL, 10); + if (errno) + goto convertion_failed; + + /* TODO Implemet IDs */ + ges_base_xml_formatter_add_track (GES_BASE_XML_FORMATTER (self), track_type, + caps, strtrack_id, NULL, metadatas, error); + + return; + +wrong_caps: + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "element '%s', Can not create caps: %s'", element_name, strcaps); + return; + +convertion_failed: + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "element '%s', Wrong property type, error: %s'", element_name, + g_strerror (errno)); + return; + +} + +static inline void +_parse_layer (GMarkupParseContext * context, const gchar * element_name, + const gchar ** attribute_names, const gchar ** attribute_values, + GESXmlFormatter * self, GError ** error) +{ + GstStructure *props = NULL; + guint priority; + GType extractable_type = G_TYPE_NONE; + const gchar *metadatas = NULL, *properties = NULL, *strprio = NULL, + *extractable_type_name; + + if (!g_markup_collect_attributes (element_name, attribute_names, + attribute_values, error, + G_MARKUP_COLLECT_STRING, "priority", &strprio, + COLLECT_STR_OPT, "extractable-type-name", &extractable_type_name, + COLLECT_STR_OPT, "properties", &properties, + COLLECT_STR_OPT, "metadatas", &metadatas, G_MARKUP_COLLECT_INVALID)) + return; + + if (extractable_type_name) { + extractable_type = g_type_from_name (extractable_type_name); + if (extractable_type == G_TYPE_NONE) { + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "element '%s' invalid extractable_type %s'", + element_name, extractable_type_name); + + return; + } else if (!g_type_is_a (extractable_type, GES_TYPE_EXTRACTABLE)) { + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "element '%s', %s not an extractable_type'", + element_name, extractable_type_name); + + return; + } + } + + if (properties) { + props = gst_structure_from_string (properties, NULL); + if (props == NULL) + goto wrong_properties; + } + + priority = g_ascii_strtoll (strprio, NULL, 10); + if (errno) + goto convertion_failed; + + ges_base_xml_formatter_add_layer (GES_BASE_XML_FORMATTER (self), + extractable_type, priority, props, metadatas, error); + if (props) + gst_structure_free (props); + + return; + +convertion_failed: + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "element '%s', Wrong property type, error: %s'", element_name, + g_strerror (errno)); + return; + +wrong_properties: + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "element '%s', wrong layer properties '%s', could no be deserialized", + element_name, properties); +} + +static inline void +_parse_timeline_object (GMarkupParseContext * context, + const gchar * element_name, const gchar ** attribute_names, + const gchar ** attribute_values, GESXmlFormatter * self, GError ** error) +{ + GType type; + gdouble rate = 0; + GstStructure *props = NULL; + GESTrackType track_types; + GstClockTime start, inpoint = 0, duration, layer_prio; + + const gchar *strid, *asset_id, *strstart, *strin, *strduration, *strrate, + *strtrack_types, *strtype, *metadatas = NULL, *properties = + NULL, *strlayer_prio; + + if (!g_markup_collect_attributes (element_name, attribute_names, + attribute_values, error, + G_MARKUP_COLLECT_STRING, "id", &strid, + G_MARKUP_COLLECT_STRING, "type-name", &strtype, + G_MARKUP_COLLECT_STRING, "start", &strstart, + G_MARKUP_COLLECT_STRING, "duration", &strduration, + G_MARKUP_COLLECT_STRING, "asset-id", &asset_id, + G_MARKUP_COLLECT_STRING, "track-types", &strtrack_types, + G_MARKUP_COLLECT_STRING, "layer-priority", &strlayer_prio, + COLLECT_STR_OPT, "properties", &properties, + COLLECT_STR_OPT, "metadatas", &metadatas, + COLLECT_STR_OPT, "rate", &strrate, + COLLECT_STR_OPT, "inpoint", &strin, G_MARKUP_COLLECT_INVALID)) { + return; + } + type = g_type_from_name (strtype); + if (!g_type_is_a (type, GES_TYPE_TIMELINE_OBJECT)) + goto wrong_type; + + track_types = g_ascii_strtoll (strtrack_types, NULL, 10); + if (errno) + goto convertion_failed; + + layer_prio = g_ascii_strtoll (strlayer_prio, NULL, 10); + if (errno) + goto convertion_failed; + + if (strrate) { + rate = g_ascii_strtod (strrate, NULL); + if (errno) + goto convertion_failed; + } + + if (strin) { + inpoint = g_ascii_strtoull (strin, NULL, 10); + if (errno) + goto convertion_failed; + } + + start = g_ascii_strtoull (strstart, NULL, 10); + if (errno) + goto convertion_failed; + + duration = g_ascii_strtoull (strduration, NULL, 10); + if (errno) + goto convertion_failed; + + if (properties) { + props = gst_structure_from_string (properties, NULL); + if (props == NULL) + goto wrong_properties; + } + + ges_base_xml_formatter_add_timeline_object (GES_BASE_XML_FORMATTER (self), + strid, asset_id, type, start, inpoint, duration, rate, layer_prio, + track_types, props, metadatas, error); + if (props) + gst_structure_free (props); + + return; + +wrong_properties: + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "element '%s', TimelineObject %s properties '%s', could no be deserialized", + element_name, asset_id, properties); + return; + +convertion_failed: + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "element '%s', Wrong property type, error: %s'", element_name, + g_strerror (errno)); + return; + +wrong_type: + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "element '%s', %s not a GESTimelineObject'", element_name, strtype); +} + +static inline void +_parse_effect (GMarkupParseContext * context, const gchar * element_name, + const gchar ** attribute_names, const gchar ** attribute_values, + GESXmlFormatter * self, GError ** error) +{ + GType type; + + GstStructure *children_props = NULL, *props = NULL; + const gchar *asset_id = NULL, *strtype = NULL, *track_id = + NULL, *metadatas = NULL, *properties = NULL, *track_type = NULL, + *children_properties = NULL, *tlobj_id; + + if (!g_markup_collect_attributes (element_name, attribute_names, + attribute_values, error, + COLLECT_STR_OPT, "metadatas", &metadatas, + G_MARKUP_COLLECT_STRING, "asset-id", &asset_id, + G_MARKUP_COLLECT_STRING, "timeline-object-id", &tlobj_id, + G_MARKUP_COLLECT_STRING, "type-name", &strtype, + G_MARKUP_COLLECT_STRING, "track-id", &track_id, + COLLECT_STR_OPT, "children-properties", &children_properties, + COLLECT_STR_OPT, "track-type", &track_type, + COLLECT_STR_OPT, "properties", &properties, + G_MARKUP_COLLECT_INVALID)) { + return; + } + + type = g_type_from_name (strtype); + if (!g_type_is_a (type, GES_TYPE_TRACK_EFFECT)) + goto wrong_type; + + if (children_properties) { + children_props = gst_structure_from_string (children_properties, NULL); + if (children_props == NULL) + goto wrong_children_properties; + } + + if (properties) { + props = gst_structure_from_string (properties, NULL); + if (props == NULL) + goto wrong_properties; + } + + ges_base_xml_formatter_add_track_object (GES_BASE_XML_FORMATTER (self), + type, asset_id, track_id, tlobj_id, children_props, props, metadatas, + error); + + if (props) + gst_structure_free (props); + if (children_props) + gst_structure_free (children_props); + + return; + +wrong_properties: + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "element '%s', Effect %s properties '%s', could no be deserialized", + element_name, asset_id, properties); + return; + +wrong_children_properties: + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "element '%s', Effect %s children properties '%s', could no be deserialized", + element_name, asset_id, children_properties); + return; + +wrong_type: + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "element '%s', %s not a GESTrackEffect'", element_name, strtype); +} + +static void +_parse_element_start (GMarkupParseContext * context, const gchar * element_name, + const gchar ** attribute_names, const gchar ** attribute_values, + gpointer self, GError ** error) +{ + GESXmlFormatterPrivate *priv = _GET_PRIV (self); + + if (!G_UNLIKELY (priv->ges_opened)) + _parse_ges_element (context, element_name, attribute_names, + attribute_values, self, error); + else if (!G_UNLIKELY (priv->project_opened)) + _parse_project (context, element_name, attribute_names, attribute_values, + self, error); + else if (g_strcmp0 (element_name, "encoding-profile") == 0) + _parse_encoding_profile (context, element_name, attribute_names, + attribute_values, self, error); + else if (g_strcmp0 (element_name, "stream-profile") == 0) + _parse_stream_profile (context, element_name, attribute_names, + attribute_values, self, error); + else if (g_strcmp0 (element_name, "timeline") == 0) + _parse_timeline (context, element_name, attribute_names, attribute_values, + self, error); + else if (g_strcmp0 (element_name, "asset") == 0) + _parse_asset (context, element_name, attribute_names, attribute_values, + self, error); + else if (g_strcmp0 (element_name, "track") == 0) + _parse_track (context, element_name, attribute_names, + attribute_values, self, error); + else if (g_strcmp0 (element_name, "layer") == 0) + _parse_layer (context, element_name, attribute_names, + attribute_values, self, error); + else if (g_strcmp0 (element_name, "timeline-object") == 0) + _parse_timeline_object (context, element_name, attribute_names, + attribute_values, self, error); + else if (g_strcmp0 (element_name, "effect") == 0) + _parse_effect (context, element_name, attribute_names, + attribute_values, self, error); + else + GST_LOG_OBJECT (self, "Element %s not handled", element_name); +} + +static void +_parse_element_end (GMarkupParseContext * context, + const gchar * element_name, gpointer self, GError ** error) +{ + /*GESXmlFormatterPrivate *priv = _GET_PRIV (self); */ +} + +static void +_error_parsing (GMarkupParseContext * context, GError * error, + gpointer user_data) +{ + GST_WARNING ("Error accured when parsing %s", error->message); +} + +/*********************************************** + * * + * Saving implementation * + * * + ***********************************************/ + +/* XML writting utils */ +static inline void +append_printf_escaped (GString * str, const gchar * format, ...) +{ + gchar *tmp; + va_list args; + + va_start (args, format); + tmp = g_markup_vprintf_escaped (format, args); + va_end (args); + + g_string_append (str, tmp); + g_free (tmp); +} + +static inline gboolean +_can_serialize_spec (GParamSpec * spec) +{ + if (spec->flags & G_PARAM_WRITABLE + && !g_type_is_a (G_PARAM_SPEC_VALUE_TYPE (spec), G_TYPE_OBJECT) + && g_strcmp0 (spec->name, "name") + && G_PARAM_SPEC_VALUE_TYPE (spec) != G_TYPE_GTYPE) + return TRUE; + + return FALSE; +} + +static inline void +_init_value_from_spec_for_serialization (GValue * value, GParamSpec * spec) +{ + + if (g_type_is_a (spec->value_type, G_TYPE_ENUM) || + g_type_is_a (spec->value_type, G_TYPE_FLAGS)) + g_value_init (value, G_TYPE_INT); + else + g_value_init (value, spec->value_type); +} + +static gchar * +_serialize_properties (GObject * object, const gchar * fieldname, ...) +{ + gchar *ret; + guint n_props, j; + GParamSpec *spec, **pspecs; + GObjectClass *class = G_OBJECT_GET_CLASS (object); + GstStructure *structure = gst_structure_new_empty ("properties"); + + pspecs = g_object_class_list_properties (class, &n_props); + for (j = 0; j < n_props; j++) { + GValue val = { 0 }; + + spec = pspecs[j]; + if (_can_serialize_spec (spec)) { + _init_value_from_spec_for_serialization (&val, spec); + g_object_get_property (object, spec->name, &val); + gst_structure_set_value (structure, spec->name, &val); + g_value_unset (&val); + } + } + g_free (pspecs); + + if (fieldname) { + va_list varargs; + va_start (varargs, fieldname); + gst_structure_remove_fields_valist (structure, fieldname, varargs); + va_end (varargs); + } + + ret = gst_structure_to_string (structure); + gst_structure_free (structure); + + return ret; +} + +static inline void +_save_assets (GString * str, GESProject * project) +{ + char *properties, *metas; + GESAsset *asset; + GList *assets, *tmp; + + assets = ges_project_list_assets (project, GES_TYPE_EXTRACTABLE); + for (tmp = assets; tmp; tmp = tmp->next) { + asset = GES_ASSET (tmp->data); + properties = _serialize_properties (G_OBJECT (asset), NULL); + metas = ges_meta_container_metas_to_string (GES_META_CONTAINER (asset)); + append_printf_escaped (str, + "\n", + ges_asset_get_id (asset), + g_type_name (ges_asset_get_extractable_type (asset)), properties, + metas); + g_free (properties); + g_free (metas); + } + g_list_free_full (assets, gst_object_unref); +} + +static inline void +_save_tracks (GString * str, GESTimeline * timeline) +{ + gchar *strtmp, *metas; + GESTrack *track; + GList *tmp, *tracks; + + guint nb_tracks = 0; + + tracks = ges_timeline_get_tracks (timeline); + for (tmp = tracks; tmp; tmp = tmp->next) { + track = GES_TRACK (tmp->data); + strtmp = gst_caps_to_string (ges_track_get_caps (track)); + metas = ges_meta_container_metas_to_string (GES_META_CONTAINER (track)); + append_printf_escaped (str, + "\n", + strtmp, track->type, nb_tracks++, metas); + g_free (strtmp); + g_free (metas); + } + g_list_free_full (tracks, gst_object_unref); +} + +static inline void +_save_effect (GString * str, guint tlobj_id, GESTrackObject * tckobj, + GESTimeline * timeline) +{ + GESTrack *tck; + GList *tmp, *tracks; + GstStructure *structure; + gchar *properties, *metas; + GParamSpec **pspecs, *spec; + guint j, n_props = 0, track_id = 0; + + tck = ges_track_object_get_track (tckobj); + if (tck == NULL) { + GST_WARNING_OBJECT (tckobj, " Not in any track, can not save it"); + + return; + } + + tracks = ges_timeline_get_tracks (timeline); + for (tmp = tracks; tmp; tmp = tmp->next) { + if (tmp->data == tck) + break; + track_id++; + } + g_list_free_full (tracks, gst_object_unref); + + properties = _serialize_properties (G_OBJECT (tckobj), "start", + "in-point", "duration", "locked", "max-duration", "name", NULL); + metas = ges_meta_container_metas_to_string (GES_META_CONTAINER (tckobj)); + append_printf_escaped (str, "type, track_id, properties, + metas); + g_free (properties); + g_free (metas); + + pspecs = ges_track_object_list_children_properties (tckobj, &n_props); + structure = gst_structure_new_empty ("properties"); + for (j = 0; j < n_props; j++) { + GValue val = { 0 }; + + spec = pspecs[j]; + if (_can_serialize_spec (spec)) { + _init_value_from_spec_for_serialization (&val, spec); + ges_track_object_get_child_property_by_pspec (tckobj, spec, &val); + gst_structure_set_value (structure, spec->name, &val); + g_value_unset (&val); + } + g_param_spec_unref (spec); + } + g_free (pspecs); + + append_printf_escaped (str, " children-properties='%s'/>\n", + gst_structure_to_string (structure)); + gst_structure_free (structure); +} + +static inline void +_save_layers (GString * str, GESTimeline * timeline) +{ + gchar *properties, *metas; + GESTimelineLayer *layer; + GESTimelineObject *tlobj; + GList *tmplayer, *tmptlobj, *tlobjs; + + guint nbtlobjs = 0; + + for (tmplayer = timeline->layers; tmplayer; tmplayer = tmplayer->next) { + guint priority; + layer = GES_TIMELINE_LAYER (tmplayer->data); + + priority = ges_timeline_layer_get_priority (layer); + properties = _serialize_properties (G_OBJECT (layer), "priority", NULL); + metas = ges_meta_container_metas_to_string (GES_META_CONTAINER (layer)); + append_printf_escaped (str, + "\n", priority, + properties, metas); + g_free (properties); + g_free (metas); + + tlobjs = ges_timeline_layer_get_objects (layer); + for (tmptlobj = tlobjs; tmptlobj; tmptlobj = tmptlobj->next) { + GList *effects, *tmpeffect; + + tlobj = GES_TIMELINE_OBJECT (tmptlobj->data); + effects = ges_timeline_object_get_top_effects (tlobj); + + /* We escape all mandatrorry properties that are handled sparetely + * and vtype for StandarTransition as it is the asset ID */ + properties = _serialize_properties (G_OBJECT (tlobj), + "supported-formats", "rate", "in-point", "start", "duration", + "max-duration", "priority", "vtype", "uri", NULL); + append_printf_escaped (str, + "\n", nbtlobjs, + ges_extractable_get_id (GES_EXTRACTABLE (tlobj)), + g_type_name (G_OBJECT_TYPE (tlobj)), priority, + ges_timeline_object_get_supported_formats (tlobj), tlobj->start, + tlobj->duration, tlobj->inpoint, 0, properties); + g_free (properties); + + for (tmpeffect = effects; tmpeffect; tmpeffect = tmpeffect->next) + _save_effect (str, nbtlobjs, GES_TRACK_OBJECT (tmpeffect->data), + timeline); + g_string_append (str, "\n"); + nbtlobjs++; + } + g_string_append (str, "\n"); + } +} + + +static inline void +_save_timeline (GString * str, GESTimeline * timeline) +{ + gchar *properties = NULL, *metas = NULL; + + properties = _serialize_properties (G_OBJECT (timeline), "update", "name", + "async-handling", "message-forward", NULL); + + metas = ges_meta_container_metas_to_string (GES_META_CONTAINER (timeline)); + append_printf_escaped (str, "\n", + properties, metas); + + _save_tracks (str, timeline); + _save_layers (str, timeline); + + g_string_append (str, "\n"); + + g_free (properties); + g_free (metas); +} + +static void +_save_stream_profiles (GString * str, GstEncodingProfile * sprof, + const gchar * profilename, guint id) +{ + gchar *tmpc; + GstCaps *tmpcaps; + const gchar *preset, *preset_name, *name, *description; + + append_printf_escaped (str, "\n"); +} + +static inline void +_save_encoding_profiles (GString * str, GESProject * project) +{ + GstCaps *profformat; + const gchar *profname, *profdesc, *profpreset, *proftype, *profpresetname; + + const GList *tmp; + + for (tmp = ges_project_list_encoding_profiles (project); tmp; tmp = tmp->next) { + GstEncodingProfile *prof = GST_ENCODING_PROFILE (tmp->data); + + profname = gst_encoding_profile_get_name (prof); + profdesc = gst_encoding_profile_get_description (prof); + profpreset = gst_encoding_profile_get_preset (prof); + profpresetname = gst_encoding_profile_get_preset_name (prof); + proftype = gst_encoding_profile_get_type_nick (prof); + + append_printf_escaped (str, + "\n"); + + if (GST_IS_ENCODING_CONTAINER_PROFILE (prof)) { + guint i = 0; + const GList *tmp2; + GstEncodingContainerProfile *container_prof; + + container_prof = GST_ENCODING_CONTAINER_PROFILE (prof); + for (tmp2 = gst_encoding_container_profile_get_profiles (container_prof); + tmp2; tmp2 = tmp2->next, i++) { + GstEncodingProfile *sprof = (GstEncodingProfile *) tmp2->data; + _save_stream_profiles (str, sprof, profname, i); + } + } + append_printf_escaped (str, "\n", NULL); + } +} + +static GString * +_save (GESFormatter * formatter, GESTimeline * timeline, GError ** error) +{ + GString *str; + GESProject *project; + + gchar *properties = NULL, *metas = NULL; + GESXmlFormatterPrivate *priv; + + + priv = _GET_PRIV (formatter); + project = formatter->project; + str = priv->str = g_string_new (NULL); + + g_string_append_printf (str, "\n", API_VERSION, + MINOR_VERSION); + properties = _serialize_properties (G_OBJECT (project), NULL); + metas = ges_meta_container_metas_to_string (GES_META_CONTAINER (project)); + append_printf_escaped (str, "\n", + properties, metas); + g_free (properties); + g_free (metas); + + g_string_append (str, "\n"); + _save_encoding_profiles (str, project); + g_string_append (str, "\n"); + + g_string_append (str, "\n"); + _save_assets (str, project); + g_string_append (str, "\n"); + + _save_timeline (str, timeline); + g_string_append (str, "\n"); + + priv->str = NULL; + + return str; +} + +/*********************************************** + * * + * GObject virtual methods implementation * + * * + ***********************************************/ + +static void +_get_property (GObject * object, guint property_id, + GValue * value, GParamSpec * pspec) +{ +} + +static void +_set_property (GObject * object, guint property_id, + const GValue * value, GParamSpec * pspec) +{ +} + +static void +ges_xml_formatter_init (GESXmlFormatter * self) +{ + GESXmlFormatterPrivate *priv = _GET_PRIV (self); + + priv->project_opened = FALSE; +} + +static void +ges_xml_formatter_class_init (GESXmlFormatterClass * self_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (self_class); + GESBaseXmlFormatterClass *basexmlformatter_class; + + basexmlformatter_class = GES_BASE_XML_FORMATTER_CLASS (self_class); + + g_type_class_add_private (self_class, sizeof (GESXmlFormatterPrivate)); + object_class->get_property = _get_property; + object_class->set_property = _set_property; + + basexmlformatter_class->content_parser.start_element = _parse_element_start; + basexmlformatter_class->content_parser.end_element = _parse_element_end; + basexmlformatter_class->content_parser.text = NULL; + basexmlformatter_class->content_parser.passthrough = NULL; + basexmlformatter_class->content_parser.error = _error_parsing; + + ges_formatter_class_register_metas (GES_FORMATTER_CLASS (self_class), + "ges", "The GStreamer Editing Services file format", + "xges", "application/ges", VERSION, GST_RANK_PRIMARY); + + basexmlformatter_class->save = _save; +} + +#undef COLLECT_STR_OPT diff --git a/ges/ges-xml-formatter.h b/ges/ges-xml-formatter.h new file mode 100644 index 0000000000..ef427a8c22 --- /dev/null +++ b/ges/ges-xml-formatter.h @@ -0,0 +1,51 @@ +/* Gstreamer Editing Services + * + * Copyright (C) <2012> Thibault Saunier + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "ges-base-xml-formatter.h" + +#ifndef GES_XML_FORMATTER_H +#define GES_XML_FORMATTER_H + +G_BEGIN_DECLS +#define GES_TYPE_XML_FORMATTER (ges_xml_formatter_get_type ()) +#define GES_XML_FORMATTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_XML_FORMATTER, GESXmlFormatter)) +#define GES_XML_FORMATTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_XML_FORMATTER, GESXmlFormatterClass)) +#define GES_IS_XML_FORMATTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_XML_FORMATTER)) +#define GES_IS_XML_FORMATTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_XML_FORMATTER)) +#define GES_XML_FORMATTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_XML_FORMATTER, GESXmlFormatterClass)) +typedef struct _GESXmlFormatterPrivate GESXmlFormatterPrivate; + +typedef struct +{ + GESBaseXmlFormatter parent; + + GESXmlFormatterPrivate *priv; +} GESXmlFormatter; + +typedef struct +{ + GESBaseXmlFormatterClass parent; + +} GESXmlFormatterClass; + +GType ges_xml_formatter_get_type (void); + +G_END_DECLS +#endif /* _GES_XML_FORMATTER_H */ diff --git a/ges/ges.c b/ges/ges.c index 59c19eda87..af8cb42cb0 100644 --- a/ges/ges.c +++ b/ges/ges.c @@ -72,6 +72,10 @@ ges_init (void) /* register formatter types with the system */ GES_TYPE_PITIVI_FORMATTER; + GES_TYPE_XML_FORMATTER; + + /* Register track effect */ + GES_TYPE_TRACK_PARSE_LAUNCH_EFFECT; GES_TYPE_META_CONTAINER; diff --git a/ges/ges.h b/ges/ges.h index f4668f7c86..82c48f4a3e 100644 --- a/ges/ges.h +++ b/ges/ges.h @@ -49,6 +49,8 @@ #include #include #include +#include +#include #include #include