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.
25 #include "ges-internal.h"
27 GST_DEBUG_CATEGORY_STATIC (base_xml_formatter);
28 #undef GST_CAT_DEFAULT
29 #define GST_CAT_DEFAULT base_xml_formatter
31 #define parent_class ges_base_xml_formatter_parent_class
34 (((GESBaseXmlFormatter*) o)->priv)
37 static gboolean _loading_done_cb (GESFormatter * self);
39 typedef struct PendingEffects
42 GESTrackElement *trackelement;
43 GstStructure *children_properties;
44 GstStructure *properties;
48 typedef struct PendingBinding
51 GstControlSource *source;
56 typedef struct PendingChildProperties
59 GstStructure *structure;
60 } PendingChildProperties;
62 typedef struct PendingGroup
66 GList *pending_children;
69 typedef struct PendingClip
76 GstClockTime duration;
77 GESTrackType track_types;
80 GstStructure *properties;
81 GstStructure *children_properties;
86 GList *pending_bindings;
88 GList *children_props;
90 /* TODO Implement asset effect management
91 * PendingTrackElements *track_elements; */
94 typedef struct LayerEntry
100 typedef struct PendingAsset
102 GESFormatter *formatter;
104 GstStructure *properties;
108 struct _GESBaseXmlFormatterPrivate
110 GMarkupParseContext *parsecontext;
113 /* Asset.id -> PendingClip */
114 GHashTable *assetid_pendingclips;
116 /* Clip.ID -> Pending */
117 GHashTable *clipid_pendings;
119 /* Clip.ID -> Clip */
120 GHashTable *containers;
125 /* layer.prio -> LayerEntry */
128 /* List of asset waited to be created */
129 GList *pending_assets;
131 /* current track element */
132 GESTrackElement *current_track_element;
134 GESClip *current_clip;
135 PendingClip *current_pending_clip;
137 gboolean timeline_auto_transition;
143 _free_pending_clip (GESBaseXmlFormatterPrivate * priv, PendingClip * pend);
146 _free_layer_entry (LayerEntry * entry)
148 gst_object_unref (entry->layer);
149 g_slice_free (LayerEntry, entry);
153 _free_pending_group (PendingGroup * pgroup)
156 g_object_unref (pgroup->group);
157 g_list_free_full (pgroup->pending_children, g_free);
158 g_slice_free (PendingGroup, pgroup);
167 static GParamSpec *properties[PROP_LAST];
173 static guint signals[LAST_SIGNAL];
176 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GESBaseXmlFormatter,
177 ges_base_xml_formatter, GES_TYPE_FORMATTER);
179 static GMarkupParseContext *
180 create_parser_context (GESBaseXmlFormatter * self, const gchar * uri,
185 gchar *xmlcontent = NULL;
186 GMarkupParseContext *parsecontext = NULL;
187 GESBaseXmlFormatterClass *self_class =
188 GES_BASE_XML_FORMATTER_GET_CLASS (self);
192 GST_DEBUG_OBJECT (self, "loading xml from %s", uri);
194 file = g_file_new_for_uri (uri);
196 /* TODO Handle GCancellable */
197 if (!g_file_query_exists (file, NULL)) {
198 err = g_error_new (GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
199 "Invalid URI: \"%s\"", uri);
203 if (!g_file_load_contents (file, NULL, &xmlcontent, &xmlsize, NULL, &err))
206 if (g_strcmp0 (xmlcontent, "") == 0)
209 parsecontext = g_markup_parse_context_new (&self_class->content_parser,
210 G_MARKUP_TREAT_CDATA_AS_TEXT, self, NULL);
212 if (g_markup_parse_context_parse (parsecontext, xmlcontent, xmlsize,
216 if (!g_markup_parse_context_end_parse (parsecontext, &err))
222 g_object_unref (file);
227 GST_WARNING ("failed to load contents from \"%s\"", uri);
228 g_propagate_error (error, err);
231 g_markup_parse_context_free (parsecontext);
238 /***********************************************
240 * GESFormatter virtual methods implementation *
242 ***********************************************/
245 _can_load_uri (GESFormatter * dummy_formatter, const gchar * uri,
248 GMarkupParseContext *ctx;
249 GESBaseXmlFormatter *self = GES_BASE_XML_FORMATTER (dummy_formatter);
251 /* we create a temporary object so we can use it as a context */
252 _GET_PRIV (self)->check_only = TRUE;
255 ctx = create_parser_context (self, uri, error);
259 g_markup_parse_context_free (ctx);
264 _load_from_uri (GESFormatter * self, GESTimeline * timeline, const gchar * uri,
267 GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
269 ges_timeline_set_auto_transition (timeline, FALSE);
272 create_parser_context (GES_BASE_XML_FORMATTER (self), uri, error);
274 if (!priv->parsecontext)
277 if (g_hash_table_size (priv->assetid_pendingclips) == 0 &&
278 priv->pending_assets == NULL)
279 g_idle_add ((GSourceFunc) _loading_done_cb, g_object_ref (self));
285 _save_to_uri (GESFormatter * formatter, GESTimeline * timeline,
286 const gchar * uri, gboolean overwrite, GError ** error)
291 GOutputStream *stream;
292 GError *lerror = NULL;
294 g_return_val_if_fail (formatter->project, FALSE);
296 file = g_file_new_for_uri (uri);
297 stream = G_OUTPUT_STREAM (g_file_create (file, G_FILE_CREATE_NONE, NULL,
299 if (stream == NULL) {
300 if (overwrite && lerror->code == G_IO_ERROR_EXISTS) {
301 g_clear_error (&lerror);
302 stream = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE,
303 G_FILE_CREATE_NONE, NULL, &lerror));
307 goto failed_opening_file;
310 str = GES_BASE_XML_FORMATTER_GET_CLASS (formatter)->save (formatter,
314 goto serialization_failed;
316 ret = g_output_stream_write_all (stream, str->str, str->len, NULL,
318 ret = g_output_stream_close (stream, NULL, &lerror);
321 GST_WARNING_OBJECT (formatter, "Could not save %s because: %s", uri,
324 g_string_free (str, TRUE);
325 gst_object_unref (file);
326 gst_object_unref (stream);
329 g_propagate_error (error, lerror);
333 serialization_failed:
334 gst_object_unref (file);
336 g_output_stream_close (stream, NULL, NULL);
337 gst_object_unref (stream);
339 g_propagate_error (error, lerror);
344 gst_object_unref (file);
346 GST_WARNING_OBJECT (formatter, "Could not open %s because: %s", uri,
350 g_propagate_error (error, lerror);
355 /***********************************************
357 * GOBject virtual methods implementation *
359 ***********************************************/
362 _dispose (GObject * object)
364 GESBaseXmlFormatterPrivate *priv = _GET_PRIV (object);
365 GList *pendings, *pending_clips_lists;
367 pending_clips_lists = g_hash_table_get_values (priv->assetid_pendingclips);
368 for (pendings = pending_clips_lists; pendings; pendings = pendings->next)
369 g_list_free_full (pendings, (GDestroyNotify) _free_pending_clip);
370 g_list_free (pending_clips_lists);
372 g_clear_pointer (&priv->assetid_pendingclips, g_hash_table_unref);
373 g_clear_pointer (&priv->containers, g_hash_table_unref);
374 g_clear_pointer (&priv->clipid_pendings, g_hash_table_unref);
375 g_clear_pointer (&priv->tracks, g_hash_table_unref);
376 g_clear_pointer (&priv->layers, g_hash_table_unref);
378 G_OBJECT_CLASS (parent_class)->dispose (object);
382 _finalize (GObject * object)
384 GESBaseXmlFormatterPrivate *priv = _GET_PRIV (object);
386 if (priv->parsecontext != NULL)
387 g_markup_parse_context_free (priv->parsecontext);
389 g_list_free_full (priv->groups, (GDestroyNotify) _free_pending_group);
392 G_OBJECT_CLASS (parent_class)->finalize (object);
396 ges_base_xml_formatter_init (GESBaseXmlFormatter * self)
398 GESBaseXmlFormatterPrivate *priv;
400 self->priv = ges_base_xml_formatter_get_instance_private (self);
404 priv->check_only = FALSE;
405 priv->parsecontext = NULL;
406 priv->pending_assets = NULL;
408 /* The PendingClip are owned by the assetid_pendingclips table */
409 priv->assetid_pendingclips = g_hash_table_new_full (g_str_hash,
410 g_str_equal, g_free, NULL);
411 priv->clipid_pendings = g_hash_table_new_full (g_str_hash,
412 g_str_equal, g_free, NULL);
413 priv->containers = g_hash_table_new_full (g_str_hash,
414 g_str_equal, g_free, gst_object_unref);
415 priv->tracks = g_hash_table_new_full (g_str_hash,
416 g_str_equal, g_free, gst_object_unref);
417 priv->layers = g_hash_table_new_full (g_direct_hash,
418 g_direct_equal, NULL, (GDestroyNotify) _free_layer_entry);
419 priv->current_track_element = NULL;
420 priv->current_clip = NULL;
421 priv->current_pending_clip = NULL;
422 priv->timeline_auto_transition = FALSE;
426 ges_base_xml_formatter_class_init (GESBaseXmlFormatterClass * self_class)
428 GESFormatterClass *formatter_klass = GES_FORMATTER_CLASS (self_class);
429 GObjectClass *object_class = G_OBJECT_CLASS (self_class);
431 object_class->dispose = _dispose;
432 object_class->finalize = _finalize;
434 formatter_klass->can_load_uri = _can_load_uri;
435 formatter_klass->load_from_uri = _load_from_uri;
436 formatter_klass->save_to_uri = _save_to_uri;
438 self_class->save = NULL;
440 GST_DEBUG_CATEGORY_INIT (base_xml_formatter, "base-xml-formatter",
441 GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "Base XML Formatter");
444 /***********************************************
448 ***********************************************/
451 static GESTrackElement *
452 _get_element_by_track_id (GESBaseXmlFormatterPrivate * priv,
453 const gchar * track_id, GESClip * clip)
455 GESTrack *track = g_hash_table_lookup (priv->tracks, track_id);
457 return ges_clip_find_track_element (clip, track, GES_TYPE_SOURCE);
461 _set_auto_transition (gpointer prio, LayerEntry * entry, gpointer udata)
463 ges_layer_set_auto_transition (entry->layer, entry->auto_trans);
467 _add_all_groups (GESFormatter * self)
470 GESTimelineElement *child;
471 GESBaseXmlFormatterPrivate *priv = GES_BASE_XML_FORMATTER (self)->priv;
473 for (tmp = priv->groups; tmp; tmp = tmp->next) {
475 PendingGroup *pgroup = tmp->data;
477 timeline_add_group (self->timeline, pgroup->group);
479 for (lchild = ((PendingGroup *) tmp->data)->pending_children; lchild;
480 lchild = lchild->next) {
481 child = g_hash_table_lookup (priv->containers, lchild->data);
483 GST_DEBUG_OBJECT (tmp->data, "Adding %s child %" GST_PTR_FORMAT " %s",
484 (const gchar *) lchild->data, child,
485 GES_TIMELINE_ELEMENT_NAME (child));
486 ges_container_add (GES_CONTAINER (pgroup->group), child);
488 pgroup->group = NULL;
491 g_list_free_full (priv->groups, (GDestroyNotify) _free_pending_group);
496 _loading_done (GESFormatter * self)
499 GESBaseXmlFormatterPrivate *priv = GES_BASE_XML_FORMATTER (self)->priv;
501 _add_all_groups (self);
503 if (priv->parsecontext)
504 g_markup_parse_context_free (priv->parsecontext);
505 priv->parsecontext = NULL;
507 ges_timeline_set_auto_transition (self->timeline,
508 priv->timeline_auto_transition);
510 /* Go over all assets and make sure that all proxies we were 'trying' to set are finally
512 assets = ges_project_list_assets (self->project, GES_TYPE_EXTRACTABLE);
513 for (tmp = assets; tmp; tmp = tmp->next) {
514 ges_asset_set_proxy (NULL, tmp->data);
516 g_list_free (assets);
518 g_hash_table_foreach (priv->layers, (GHFunc) _set_auto_transition, NULL);
519 ges_project_set_loaded (self->project, self);
523 _loading_done_cb (GESFormatter * self)
525 _loading_done (self);
526 gst_object_unref (self);
532 _set_child_property (GQuark field_id, const GValue * value,
533 GESTimelineElement * tlelement)
538 /* FIXME: error handling? */
539 if (!ges_timeline_element_lookup_child (tlelement,
540 g_quark_to_string (field_id), &object, &pspec)) {
541 #ifndef GST_DISABLE_GST_DEBUG
542 gchar *tmp = gst_value_serialize (value);
543 GST_ERROR_OBJECT (tlelement, "Could not set %s=%s",
544 g_quark_to_string (field_id), tmp);
550 g_object_set_property (G_OBJECT (object), pspec->name, value);
551 g_param_spec_unref (pspec);
552 gst_object_unref (object);
557 set_property_foreach (GQuark field_id, const GValue * value, GObject * object)
559 g_object_set_property (object, g_quark_to_string (field_id), value);
563 static inline GESClip *
564 _add_object_to_layer (GESBaseXmlFormatterPrivate * priv, const gchar * id,
565 GESLayer * layer, GESAsset * asset, GstClockTime start,
566 GstClockTime inpoint, GstClockTime duration,
567 GESTrackType track_types, const gchar * metadatas,
568 GstStructure * properties, GstStructure * children_properties)
570 GESClip *clip = ges_layer_add_asset (layer,
571 asset, start, inpoint, duration, track_types);
574 GST_WARNING_OBJECT (clip, "Could not add object from asset: %s",
575 ges_asset_get_id (asset));
581 ges_meta_container_add_metas_from_string (GES_META_CONTAINER (clip),
585 gst_structure_foreach (properties,
586 (GstStructureForeachFunc) set_property_foreach, clip);
588 if (children_properties)
589 gst_structure_foreach (children_properties,
590 (GstStructureForeachFunc) _set_child_property, clip);
592 g_hash_table_insert (priv->containers, g_strdup (id), gst_object_ref (clip));
597 _add_track_element (GESFormatter * self, GESClip * clip,
598 GESTrackElement * trackelement, const gchar * track_id,
599 GstStructure * children_properties, GstStructure * properties)
601 GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
602 GESTrack *track = g_hash_table_lookup (priv->tracks, track_id);
605 GST_WARNING_OBJECT (self, "No track with id %s, can not add trackelement",
607 gst_object_unref (trackelement);
611 GST_DEBUG_OBJECT (self, "Adding track_element: %" GST_PTR_FORMAT
612 " To : %" GST_PTR_FORMAT, trackelement, clip);
614 ges_container_add (GES_CONTAINER (clip), GES_TIMELINE_ELEMENT (trackelement));
615 gst_structure_foreach (children_properties,
616 (GstStructureForeachFunc) _set_child_property, trackelement);
619 /* We do not serialize the priority anymore, and we should never have. */
620 gst_structure_remove_field (properties, "priority");
621 gst_structure_foreach (properties,
622 (GstStructureForeachFunc) set_property_foreach, trackelement);
627 _free_pending_children_props (PendingChildProperties * pend)
629 g_free (pend->track_id);
631 gst_structure_free (pend->structure);
635 _free_pending_binding (PendingBinding * pend)
637 g_free (pend->propname);
638 g_free (pend->binding_type);
639 g_free (pend->track_id);
643 _free_pending_effect (PendingEffects * pend)
645 g_free (pend->track_id);
646 gst_object_unref (pend->trackelement);
647 if (pend->children_properties)
648 gst_structure_free (pend->children_properties);
649 if (pend->properties)
650 gst_structure_free (pend->properties);
652 g_slice_free (PendingEffects, pend);
656 _free_pending_clip (GESBaseXmlFormatterPrivate * priv, PendingClip * pend)
658 gst_object_unref (pend->layer);
659 if (pend->properties)
660 gst_structure_free (pend->properties);
661 g_list_free_full (pend->effects, (GDestroyNotify) _free_pending_effect);
662 g_list_free_full (pend->pending_bindings,
663 (GDestroyNotify) _free_pending_binding);
664 g_list_free_full (pend->children_props,
665 (GDestroyNotify) _free_pending_children_props);
666 g_hash_table_remove (priv->clipid_pendings, pend->id);
668 g_slice_free (PendingClip, pend);
672 _free_pending_asset (GESBaseXmlFormatterPrivate * priv, PendingAsset * passet)
674 g_free (passet->metadatas);
675 g_free (passet->proxy_id);
676 if (passet->properties)
677 gst_structure_free (passet->properties);
679 priv->pending_assets = g_list_remove (priv->pending_assets, passet);
680 g_slice_free (PendingAsset, passet);
684 _add_children_properties (GESBaseXmlFormatterPrivate * priv, GList * childprops,
687 GList *tmpchildprops;
689 for (tmpchildprops = childprops; tmpchildprops;
690 tmpchildprops = tmpchildprops->next) {
691 PendingChildProperties *pchildprops = tmpchildprops->data;
692 GESTrackElement *element =
693 _get_element_by_track_id (priv, pchildprops->track_id, clip);
694 if (element && pchildprops->structure)
695 gst_structure_foreach (pchildprops->structure,
696 (GstStructureForeachFunc) _set_child_property, element);
701 _add_pending_bindings (GESBaseXmlFormatterPrivate * priv, GList * bindings,
706 for (tmpbinding = bindings; tmpbinding; tmpbinding = tmpbinding->next) {
707 PendingBinding *pbinding = tmpbinding->data;
708 GESTrackElement *element =
709 _get_element_by_track_id (priv, pbinding->track_id, clip);
711 ges_track_element_set_control_source (element,
712 pbinding->source, pbinding->propname, pbinding->binding_type);
717 new_asset_cb (GESAsset * source, GAsyncResult * res, PendingAsset * passet)
719 GError *error = NULL;
720 gchar *possible_id = NULL;
721 GList *tmp, *pendings = NULL;
722 GESFormatter *self = passet->formatter;
723 const gchar *id = ges_asset_get_id (source);
724 GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
725 GESAsset *asset = ges_asset_request_finish (res, &error);
728 GST_LOG_OBJECT (self, "Error %s creating asset id: %s", error->message, id);
730 /* We set the metas on the Asset to give hints to the user */
731 if (passet->metadatas)
732 ges_meta_container_add_metas_from_string (GES_META_CONTAINER (source),
734 if (passet->properties)
735 gst_structure_foreach (passet->properties,
736 (GstStructureForeachFunc) set_property_foreach, source);
738 possible_id = ges_project_try_updating_id (GES_FORMATTER (self)->project,
741 if (possible_id == NULL) {
742 GST_WARNING_OBJECT (self, "Abandoning creation of asset %s with ID %s"
743 "- Error: %s", g_type_name (G_OBJECT_TYPE (source)), id,
746 pendings = g_hash_table_lookup (priv->assetid_pendingclips, id);
747 _free_pending_asset (priv, passet);
751 /* We got a possible ID replacement for that asset, create it, and
752 * make sure the assetid_pendingclips will use it */
753 ges_asset_request_async (ges_asset_get_extractable_type (source),
754 possible_id, NULL, (GAsyncReadyCallback) new_asset_cb, passet);
755 ges_project_add_loading_asset (GES_FORMATTER (self)->project,
756 ges_asset_get_extractable_type (source), possible_id);
758 pendings = g_hash_table_lookup (priv->assetid_pendingclips, id);
760 g_hash_table_remove (priv->assetid_pendingclips, id);
761 g_hash_table_insert (priv->assetid_pendingclips,
762 g_strdup (possible_id), pendings);
764 /* pendings should no be freed */
770 if (passet->proxy_id) {
771 /* We set the URI to be used as a proxy,
772 * this will finally be set as the proxy when we
773 * are done loading all assets */
774 ges_asset_try_proxy (asset, passet->proxy_id);
777 /* now that we have the GESAsset, we create the GESClips */
778 pendings = g_hash_table_lookup (priv->assetid_pendingclips, id);
779 GST_DEBUG_OBJECT (self, "Asset created with ID %s, now creating pending "
780 " Clips, nb pendings: %i", id, g_list_length (pendings));
781 for (tmp = pendings; tmp; tmp = tmp->next) {
784 PendingClip *pend = (PendingClip *) tmp->data;
787 _add_object_to_layer (priv, pend->id, pend->layer, asset,
788 pend->start, pend->inpoint, pend->duration, pend->track_types,
789 pend->metadatas, pend->properties, pend->children_properties);
794 _add_children_properties (priv, pend->children_props, clip);
795 _add_pending_bindings (priv, pend->pending_bindings, clip);
797 GST_DEBUG_OBJECT (self, "Adding %i effect to new object",
798 g_list_length (pend->effects));
799 for (tmpeffect = pend->effects; tmpeffect; tmpeffect = tmpeffect->next) {
800 PendingEffects *peffect = (PendingEffects *) tmpeffect->data;
802 /* We keep a ref as _free_pending_effect unrefs it */
803 _add_track_element (self, clip, gst_object_ref (peffect->trackelement),
804 peffect->track_id, peffect->children_properties, peffect->properties);
808 /* And now add to the project */
809 ges_project_add_asset (self->project, asset);
810 gst_object_unref (self);
812 _free_pending_asset (priv, passet);
816 gst_object_unref (asset);
818 g_free (possible_id);
820 g_clear_error (&error);
823 for (tmp = pendings; tmp; tmp = tmp->next)
824 _free_pending_clip (priv, tmp->data);
825 g_hash_table_remove (priv->assetid_pendingclips, id);
826 g_list_free (pendings);
829 if (g_hash_table_size (priv->assetid_pendingclips) == 0 &&
830 priv->pending_assets == NULL)
831 _loading_done (self);
835 get_element_for_encoding_profile (GstEncodingProfile * prof,
836 GstElementFactoryListType type)
838 GstEncodingProfile *prof_copy;
839 GstElement *encodebin;
841 GstElement *element = NULL;
843 prof_copy = gst_encoding_profile_copy (prof);
845 gst_encoding_profile_set_presence (prof_copy, 1);
846 gst_encoding_profile_set_preset (prof_copy, NULL);
848 encodebin = gst_element_factory_make ("encodebin", NULL);
849 g_object_set (encodebin, "profile", prof_copy, NULL);
851 GST_OBJECT_LOCK (encodebin);
852 for (tmp = GST_BIN (encodebin)->children; tmp; tmp = tmp->next) {
853 GstElementFactory *factory;
854 factory = gst_element_get_factory (GST_ELEMENT (tmp->data));
856 if (factory && gst_element_factory_list_is_type (factory, type)) {
857 element = GST_ELEMENT (tmp->data);
858 gst_object_ref (element);
862 GST_OBJECT_UNLOCK (encodebin);
863 gst_object_unref (encodebin);
865 gst_encoding_profile_unref (prof_copy);
870 static GstEncodingProfile *
871 _create_profile (GESBaseXmlFormatter * self,
872 const gchar * type, const gchar * parent, const gchar * name,
873 const gchar * description, GstCaps * format, const gchar * preset,
874 GstStructure * preset_properties, const gchar * preset_name, gint id,
875 guint presence, GstCaps * restriction, guint pass,
876 gboolean variableframerate, gboolean enabled)
878 GstEncodingProfile *profile = NULL;
880 if (!g_strcmp0 (type, "container")) {
881 profile = GST_ENCODING_PROFILE (gst_encoding_container_profile_new (name,
882 description, format, preset));
883 gst_encoding_profile_set_preset_name (profile, preset_name);
884 } else if (!g_strcmp0 (type, "video")) {
885 GstEncodingVideoProfile *sprof = gst_encoding_video_profile_new (format,
886 preset, restriction, presence);
888 gst_encoding_video_profile_set_variableframerate (sprof, variableframerate);
889 gst_encoding_video_profile_set_pass (sprof, pass);
891 profile = GST_ENCODING_PROFILE (sprof);
892 } else if (!g_strcmp0 (type, "audio")) {
893 profile = GST_ENCODING_PROFILE (gst_encoding_audio_profile_new (format,
894 preset, restriction, presence));
896 GST_ERROR_OBJECT (self, "Unknown profile format '%s'", type);
901 if (!g_strcmp0 (type, "video") || !g_strcmp0 (type, "audio")) {
902 gst_encoding_profile_set_name (profile, name);
903 gst_encoding_profile_set_enabled (profile, enabled);
904 gst_encoding_profile_set_description (profile, description);
905 gst_encoding_profile_set_preset_name (profile, preset_name);
908 if (preset && preset_properties) {
911 if (!g_strcmp0 (type, "container")) {
912 element = get_element_for_encoding_profile (profile,
913 GST_ELEMENT_FACTORY_TYPE_MUXER);
915 element = get_element_for_encoding_profile (profile,
916 GST_ELEMENT_FACTORY_TYPE_ENCODER);
919 if (G_UNLIKELY (!element || !GST_IS_PRESET (element))) {
920 GST_WARNING_OBJECT (element, "Element is not a GstPreset");
924 /* If the preset doesn't exist on the system, create it */
925 if (!gst_preset_load_preset (GST_PRESET (element), preset)) {
926 gst_structure_foreach (preset_properties,
927 (GstStructureForeachFunc) set_property_foreach, element);
929 if (!gst_preset_save_preset (GST_PRESET (element), preset)) {
930 GST_WARNING_OBJECT (element, "Could not save preset %s", preset);
936 gst_object_unref (element);
942 /***********************************************
946 ***********************************************/
949 ges_base_xml_formatter_add_asset (GESBaseXmlFormatter * self,
950 const gchar * id, GType extractable_type, GstStructure * properties,
951 const gchar * metadatas, const gchar * proxy_id, GError ** error)
953 PendingAsset *passet;
954 GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
956 if (priv->check_only)
959 passet = g_slice_new0 (PendingAsset);
960 passet->metadatas = g_strdup (metadatas);
961 passet->proxy_id = g_strdup (proxy_id);
962 passet->formatter = gst_object_ref (self);
964 passet->properties = gst_structure_copy (properties);
966 ges_asset_request_async (extractable_type, id, NULL,
967 (GAsyncReadyCallback) new_asset_cb, passet);
968 ges_project_add_loading_asset (GES_FORMATTER (self)->project,
969 extractable_type, id);
970 priv->pending_assets = g_list_prepend (priv->pending_assets, passet);
974 ges_base_xml_formatter_add_clip (GESBaseXmlFormatter * self,
975 const gchar * id, const char *asset_id, GType type, GstClockTime start,
976 GstClockTime inpoint, GstClockTime duration,
977 guint layer_prio, GESTrackType track_types, GstStructure * properties,
978 GstStructure * children_properties,
979 const gchar * metadatas, GError ** error)
984 GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
986 if (priv->check_only)
989 entry = g_hash_table_lookup (priv->layers, GINT_TO_POINTER (layer_prio));
991 g_set_error (error, GES_ERROR, GES_ERROR_FORMATTER_MALFORMED_INPUT_FILE,
992 "We got a Clip in a layer"
993 " that does not exist, something is wrong either in the project file or"
994 " in %s", g_type_name (G_OBJECT_TYPE (self)));
998 /* We do not want the properties that are passed to layer-add_asset to be reset */
1000 gst_structure_remove_fields (properties, "supported-formats",
1001 "inpoint", "start", "duration", NULL);
1003 asset = ges_asset_request (type, asset_id, NULL);
1004 if (asset == NULL) {
1009 real_id = ges_extractable_type_check_id (type, asset_id, error);
1010 if (real_id == NULL) {
1012 g_set_error (error, G_MARKUP_ERROR,
1013 G_MARKUP_ERROR_INVALID_CONTENT,
1014 "Object type '%s' with Asset id: %s not be created'",
1015 g_type_name (type), asset_id);
1020 pendings = g_hash_table_lookup (priv->assetid_pendingclips, asset_id);
1022 pclip = g_slice_new0 (PendingClip);
1023 GST_DEBUG_OBJECT (self, "Adding pending %p for %s, currently: %i",
1024 pclip, asset_id, g_list_length (pendings));
1026 pclip->id = g_strdup (id);
1027 pclip->track_types = track_types;
1028 pclip->duration = duration;
1029 pclip->inpoint = inpoint;
1030 pclip->start = start;
1031 pclip->layer = gst_object_ref (entry->layer);
1033 pclip->properties = properties ? gst_structure_copy (properties) : NULL;
1034 pclip->children_properties =
1035 children_properties ? gst_structure_copy (children_properties) : NULL;
1036 pclip->metadatas = g_strdup (metadatas);
1038 /* Add the new pending object to the hashtable */
1039 g_hash_table_insert (priv->assetid_pendingclips, real_id,
1040 g_list_append (pendings, pclip));
1041 g_hash_table_insert (priv->clipid_pendings, g_strdup (id), pclip);
1043 priv->current_clip = NULL;
1044 priv->current_pending_clip = pclip;
1049 nclip = _add_object_to_layer (priv, id, entry->layer,
1050 asset, start, inpoint, duration, track_types, metadatas, properties,
1051 children_properties);
1056 priv->current_clip = nclip;
1060 ges_base_xml_formatter_set_timeline_properties (GESBaseXmlFormatter * self,
1061 GESTimeline * timeline, const gchar * properties, const gchar * metadatas)
1063 GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
1064 gboolean auto_transition = FALSE;
1067 GstStructure *props = gst_structure_from_string (properties, NULL);
1070 if (gst_structure_get_boolean (props, "auto-transition",
1072 gst_structure_remove_field (props, "auto-transition");
1074 gst_structure_foreach (props,
1075 (GstStructureForeachFunc) set_property_foreach, timeline);
1076 gst_structure_free (props);
1081 ges_meta_container_add_metas_from_string (GES_META_CONTAINER (timeline),
1085 priv->timeline_auto_transition = auto_transition;
1089 ges_base_xml_formatter_add_layer (GESBaseXmlFormatter * self,
1090 GType extractable_type, guint priority, GstStructure * properties,
1091 const gchar * metadatas, GError ** error)
1096 gboolean auto_transition = FALSE;
1097 GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
1099 if (priv->check_only)
1102 if (extractable_type == G_TYPE_NONE)
1103 layer = ges_layer_new ();
1105 asset = ges_asset_request (extractable_type, NULL, error);
1106 if (asset == NULL) {
1107 if (error && *error == NULL) {
1108 g_set_error (error, G_MARKUP_ERROR,
1109 G_MARKUP_ERROR_INVALID_CONTENT,
1110 "Layer type %s could not be created'",
1111 g_type_name (extractable_type));
1115 layer = GES_LAYER (ges_asset_extract (asset, error));
1118 ges_layer_set_priority (layer, priority);
1119 ges_timeline_add_layer (GES_FORMATTER (self)->timeline, layer);
1121 if (gst_structure_get_boolean (properties, "auto-transition",
1123 gst_structure_remove_field (properties, "auto-transition");
1125 gst_structure_foreach (properties,
1126 (GstStructureForeachFunc) set_property_foreach, layer);
1130 ges_meta_container_add_metas_from_string (GES_META_CONTAINER (layer),
1133 entry = g_slice_new0 (LayerEntry);
1134 entry->layer = gst_object_ref (layer);
1135 entry->auto_trans = auto_transition;
1137 g_hash_table_insert (priv->layers, GINT_TO_POINTER (priority), entry);
1141 ges_base_xml_formatter_add_track (GESBaseXmlFormatter * self,
1142 GESTrackType track_type, GstCaps * caps, const gchar * id,
1143 GstStructure * properties, const gchar * metadatas, GError ** error)
1146 GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
1148 if (priv->check_only) {
1152 track = ges_track_new (track_type, caps);
1153 ges_timeline_add_track (GES_FORMATTER (self)->timeline, track);
1157 GstCaps *restriction_caps;
1159 gst_structure_get (properties, "restriction-caps", G_TYPE_STRING,
1160 &restriction, NULL);
1161 gst_structure_remove_fields (properties, "restriction-caps", "caps",
1162 "message-forward", NULL);
1163 if (g_strcmp0 (restriction, "NULL")) {
1164 restriction_caps = gst_caps_from_string (restriction);
1165 ges_track_set_restriction_caps (track, restriction_caps);
1166 gst_caps_unref (restriction_caps);
1168 gst_structure_foreach (properties,
1169 (GstStructureForeachFunc) set_property_foreach, track);
1170 g_free (restriction);
1173 g_hash_table_insert (priv->tracks, g_strdup (id), gst_object_ref (track));
1175 ges_meta_container_add_metas_from_string (GES_META_CONTAINER (track),
1180 ges_base_xml_formatter_add_control_binding (GESBaseXmlFormatter * self,
1181 const gchar * binding_type, const gchar * source_type,
1182 const gchar * property_name, gint mode, const gchar * track_id,
1183 GSList * timed_values)
1185 GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
1186 GESTrackElement *element = NULL;
1188 if (track_id[0] != '-' && priv->current_clip)
1189 element = _get_element_by_track_id (priv, track_id, priv->current_clip);
1191 else if (track_id[0] != '-' && priv->current_pending_clip) {
1192 PendingBinding *pbinding;
1194 pbinding = g_slice_new0 (PendingBinding);
1195 pbinding->source = gst_interpolation_control_source_new ();
1196 g_object_set (pbinding->source, "mode", mode, NULL);
1197 gst_timed_value_control_source_set_from_list (GST_TIMED_VALUE_CONTROL_SOURCE
1198 (pbinding->source), timed_values);
1199 pbinding->propname = g_strdup (property_name);
1200 pbinding->binding_type = g_strdup (binding_type);
1201 pbinding->track_id = g_strdup (track_id);
1202 priv->current_pending_clip->pending_bindings =
1203 g_list_append (priv->current_pending_clip->pending_bindings, pbinding);
1208 element = priv->current_track_element;
1211 if (element == NULL) {
1212 GST_WARNING ("No current track element to which we can append a binding");
1216 if (!g_strcmp0 (source_type, "interpolation")) {
1217 GstControlSource *source;
1219 source = gst_interpolation_control_source_new ();
1220 ges_track_element_set_control_source (element, source,
1221 property_name, binding_type);
1223 g_object_set (source, "mode", mode, NULL);
1225 gst_timed_value_control_source_set_from_list (GST_TIMED_VALUE_CONTROL_SOURCE
1226 (source), timed_values);
1228 GST_WARNING ("This interpolation type is not supported\n");
1232 ges_base_xml_formatter_add_source (GESBaseXmlFormatter * self,
1233 const gchar * track_id, GstStructure * children_properties)
1235 GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
1236 GESTrackElement *element = NULL;
1238 if (track_id[0] != '-' && priv->current_clip)
1239 element = _get_element_by_track_id (priv, track_id, priv->current_clip);
1241 else if (track_id[0] != '-' && priv->current_pending_clip) {
1242 PendingChildProperties *pchildprops;
1244 pchildprops = g_slice_new0 (PendingChildProperties);
1245 pchildprops->track_id = g_strdup (track_id);
1246 pchildprops->structure = children_properties ?
1247 gst_structure_copy (children_properties) : NULL;
1248 priv->current_pending_clip->children_props =
1249 g_list_append (priv->current_pending_clip->children_props, pchildprops);
1252 element = priv->current_track_element;
1255 if (element == NULL) {
1257 ("No current track element to which we can append children properties");
1261 gst_structure_foreach (children_properties,
1262 (GstStructureForeachFunc) _set_child_property, element);
1266 ges_base_xml_formatter_add_track_element (GESBaseXmlFormatter * self,
1267 GType track_element_type, const gchar * asset_id, const gchar * track_id,
1268 const gchar * timeline_obj_id, GstStructure * children_properties,
1269 GstStructure * properties, const gchar * metadatas, GError ** error)
1271 GESTrackElement *trackelement;
1274 GESAsset *asset = NULL;
1275 GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
1277 if (priv->check_only)
1280 if (g_type_is_a (track_element_type, GES_TYPE_TRACK_ELEMENT) == FALSE) {
1281 GST_DEBUG_OBJECT (self, "%s is not a TrackElement, can not create it",
1282 g_type_name (track_element_type));
1286 if (g_type_is_a (track_element_type, GES_TYPE_BASE_EFFECT) == FALSE) {
1287 GST_FIXME_OBJECT (self, "%s currently not supported",
1288 g_type_name (track_element_type));
1292 asset = ges_asset_request (track_element_type, asset_id, &err);
1293 if (asset == NULL) {
1294 GST_DEBUG_OBJECT (self, "Can not create trackelement %s", asset_id);
1295 GST_FIXME_OBJECT (self, "Check if missing plugins etc %s",
1296 err ? err->message : "");
1301 trackelement = GES_TRACK_ELEMENT (ges_asset_extract (asset, NULL));
1305 ges_meta_container_add_metas_from_string (GES_META_CONTAINER
1306 (trackelement), metadatas);
1308 clip = g_hash_table_lookup (priv->containers, timeline_obj_id);
1310 _add_track_element (GES_FORMATTER (self), clip, trackelement, track_id,
1311 children_properties, properties);
1313 PendingEffects *peffect;
1314 PendingClip *pend = g_hash_table_lookup (priv->clipid_pendings,
1317 GST_WARNING_OBJECT (self, "No Clip with id: %s can not "
1318 "add TrackElement", timeline_obj_id);
1322 peffect = g_slice_new0 (PendingEffects);
1324 peffect->trackelement = trackelement;
1325 peffect->track_id = g_strdup (track_id);
1326 peffect->properties = properties ? gst_structure_copy (properties) : NULL;
1327 peffect->children_properties = children_properties ?
1328 gst_structure_copy (children_properties) : NULL;
1330 pend->effects = g_list_append (pend->effects, peffect);
1332 priv->current_track_element = trackelement;
1335 ges_project_add_asset (GES_FORMATTER (self)->project, asset);
1339 gst_object_unref (asset);
1347 ges_base_xml_formatter_add_encoding_profile (GESBaseXmlFormatter * self,
1348 const gchar * type, const gchar * parent, const gchar * name,
1349 const gchar * description, GstCaps * format, const gchar * preset,
1350 GstStructure * preset_properties, const gchar * preset_name, guint id,
1351 guint presence, GstCaps * restriction, guint pass,
1352 gboolean variableframerate, GstStructure * properties, gboolean enabled,
1356 GstEncodingProfile *profile;
1357 GstEncodingContainerProfile *parent_profile = NULL;
1358 GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
1360 if (priv->check_only)
1363 if (parent == NULL) {
1365 _create_profile (self, type, parent, name, description, format, preset,
1366 preset_properties, preset_name, id, presence, restriction, pass,
1367 variableframerate, enabled);
1368 ges_project_add_encoding_profile (GES_FORMATTER (self)->project, profile);
1369 gst_object_unref (profile);
1374 for (tmp = ges_project_list_encoding_profiles (GES_FORMATTER (self)->project);
1375 tmp; tmp = tmp->next) {
1376 GstEncodingProfile *tmpprofile = GST_ENCODING_PROFILE (tmp->data);
1378 if (g_strcmp0 (gst_encoding_profile_get_name (tmpprofile),
1379 gst_encoding_profile_get_name (tmpprofile)) == 0) {
1381 if (!GST_IS_ENCODING_CONTAINER_PROFILE (tmpprofile)) {
1382 g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
1383 "Profile '%s' parent %s is not a container...'", name, parent);
1387 parent_profile = GST_ENCODING_CONTAINER_PROFILE (tmpprofile);
1392 if (parent_profile == NULL) {
1393 g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
1394 "Profile '%s' parent %s does not exist'", name, parent);
1399 _create_profile (self, type, parent, name, description, format, preset,
1400 preset_properties, preset_name, id, presence, restriction, pass,
1401 variableframerate, enabled);
1403 if (profile == NULL)
1406 gst_encoding_container_profile_add_profile (parent_profile, profile);
1410 gst_caps_unref (format);
1412 gst_caps_unref (restriction);
1416 ges_base_xml_formatter_add_group (GESBaseXmlFormatter * self,
1417 const gchar * id, const gchar * properties, const gchar * metadatas)
1419 PendingGroup *pgroup;
1420 GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
1422 if (priv->check_only)
1425 pgroup = g_slice_new0 (PendingGroup);
1426 pgroup->group = ges_group_new ();
1429 ges_meta_container_add_metas_from_string (GES_META_CONTAINER
1430 (pgroup->group), metadatas);
1432 g_hash_table_insert (priv->containers, g_strdup (id),
1433 gst_object_ref (pgroup->group));
1434 priv->groups = g_list_prepend (priv->groups, pgroup);
1440 ges_base_xml_formatter_last_group_add_child (GESBaseXmlFormatter * self,
1441 const gchar * child_id, const gchar * name)
1443 PendingGroup *pgroup;
1444 GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
1446 if (priv->check_only)
1449 g_return_if_fail (priv->groups);
1451 pgroup = priv->groups->data;
1453 pgroup->pending_children =
1454 g_list_prepend (pgroup->pending_children, g_strdup (child_id));
1456 GST_DEBUG_OBJECT (self, "Adding %s to %s", child_id,
1457 GES_TIMELINE_ELEMENT_NAME (((PendingGroup *) priv->groups->data)->group));