From c7a984824fb6023cbbaee5830e7ea4e5ebc4284e Mon Sep 17 00:00:00 2001 From: Mathieu Duponchelle Date: Sun, 28 Aug 2011 03:55:46 +0200 Subject: [PATCH] ges: Implement a Pitivi Formatter API: ges_pitivi_formatter_new --- ges/Makefile.am | 2 + ges/ges-pitivi-formatter.c | 1018 ++++++++++++++++++++++++++++++++++++++++++++ ges/ges-pitivi-formatter.h | 61 +++ ges/ges-types.h | 2 + ges/ges.h | 4 +- 5 files changed, 1084 insertions(+), 3 deletions(-) create mode 100644 ges/ges-pitivi-formatter.c create mode 100644 ges/ges-pitivi-formatter.h diff --git a/ges/Makefile.am b/ges/Makefile.am index 55b082c..a38f726 100644 --- a/ges/Makefile.am +++ b/ges/Makefile.am @@ -46,6 +46,7 @@ libges_@GST_MAJORMINOR@_la_SOURCES = \ ges-screenshot.c \ ges-formatter.c \ ges-keyfile-formatter.c \ + ges-pitivi-formatter.c \ ges-utils.c libges_@GST_MAJORMINOR@includedir = $(includedir)/gstreamer-@GST_MAJORMINOR@/ges/ @@ -89,6 +90,7 @@ libges_@GST_MAJORMINOR@include_HEADERS = \ ges-screenshot.h \ ges-formatter.h \ ges-keyfile-formatter.h \ + ges-pitivi-formatter.h \ ges-utils.h noinst_HEADERS = \ diff --git a/ges/ges-pitivi-formatter.c b/ges/ges-pitivi-formatter.c new file mode 100644 index 0000000..590491e --- /dev/null +++ b/ges/ges-pitivi-formatter.c @@ -0,0 +1,1018 @@ +#include +#include +#define GetCurrentDir getcwd + +G_DEFINE_TYPE (GESPitiviFormatter, ges_pitivi_formatter, GES_TYPE_FORMATTER); + +static gboolean save_pitivi_timeline_to_uri (GESFormatter * pitivi_formatter, + GESTimeline * timeline, const gchar * uri); +static gboolean load_pitivi_file_from_uri (GESFormatter * self, + GESTimeline * timeline, const gchar * uri); + +static void ges_pitivi_formatter_finalize (GObject * object); + +static xmlDocPtr create_doc (const gchar * uri); + +static GHashTable *get_nodes_infos (xmlNodePtr nodes); +static gboolean create_tracks (GESFormatter * self); +static GHashTable *list_sources (GESFormatter * self); +static gboolean parse_track_objects (GESFormatter * self); +static gboolean parse_timeline_objects (GESFormatter * self); + +static void save_tracks (GESTimeline * timeline, xmlTextWriterPtr writer, + GList * source_list); +static GList *save_sources (GESTimelineLayer * layer, xmlTextWriterPtr writer); +static void save_track_objects (xmlTextWriterPtr writer, GList * source_list, + gchar * res, gint * id); +static void save_timeline_objects (xmlTextWriterPtr writer, GList * list); +static void destroy_all (GList * list); +static void create_new_source_table (gchar * key, gchar * value, + GHashTable * table); +static gboolean make_timeline_objects (GESFormatter * self); +void set_properties (GObject * obj, GHashTable * props_table); +void make_source (GList * ref_list, + GHashTable * source_table, GESFormatter * self); + +void layers_table_destroyer (gpointer data, gpointer data2, void *unused); +void list_table_destroyer (gpointer data, gpointer data2, void *unused); +void destroyer (gpointer data, gpointer data2, void *unused); +void ultimate_table_destroyer (gpointer data, gpointer data2, void *unused); +static void +track_object_added_cb (GESTimelineObject * object, + GESTrackObject * track_object, GHashTable * props_table); + +struct _GESPitiviFormatterPrivate +{ + gint not_done; + xmlXPathContextPtr xpathCtx; + GHashTable *source_table, *track_objects_table, *timeline_objects_table, + *layers_table; + GESTimeline *timeline; + gboolean parsed; + GESTrack *tracka, *trackv; + GESTimelineTestSource *background; +}; + +static void +ges_pitivi_formatter_class_init (GESPitiviFormatterClass * klass) +{ + GESFormatterClass *formatter_klass; + GObjectClass *object_class; + object_class = G_OBJECT_CLASS (klass); + formatter_klass = GES_FORMATTER_CLASS (klass); + g_type_class_add_private (klass, sizeof (GESPitiviFormatterPrivate)); + + formatter_klass->save_to_uri = save_pitivi_timeline_to_uri; + formatter_klass->load_from_uri = load_pitivi_file_from_uri; + object_class->finalize = ges_pitivi_formatter_finalize; +} + +static void +ges_pitivi_formatter_init (GESPitiviFormatter * self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + GES_TYPE_PITIVI_FORMATTER, GESPitiviFormatterPrivate); + + self->priv->not_done = 0; + + self->priv->track_objects_table = + g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); + + self->priv->timeline_objects_table = + g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); + + self->priv->layers_table = + g_hash_table_new_full (g_int_hash, g_str_equal, NULL, NULL); + + self->priv->parsed = FALSE; +} + +static void +ges_pitivi_formatter_finalize (GObject * object) +{ + GESPitiviFormatter *self = GES_PITIVI_FORMATTER (object); + GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv; + g_hash_table_foreach (priv->source_table, (GHFunc) ultimate_table_destroyer, + NULL); + g_hash_table_destroy (priv->source_table); + + g_hash_table_foreach (priv->timeline_objects_table, + (GHFunc) list_table_destroyer, NULL); + g_hash_table_destroy (priv->timeline_objects_table); + + g_hash_table_foreach (priv->layers_table, (GHFunc) layers_table_destroyer, + NULL); + g_hash_table_destroy (priv->layers_table); + + g_hash_table_foreach (priv->track_objects_table, + (GHFunc) ultimate_table_destroyer, NULL); + g_hash_table_destroy (priv->track_objects_table); + + G_OBJECT_CLASS (ges_pitivi_formatter_parent_class)->finalize (object); +} + +static gboolean +save_pitivi_timeline_to_uri (GESFormatter * pitivi_formatter, + GESTimeline * timeline, const gchar * uri) +{ + xmlTextWriterPtr writer; + GList *list = NULL, *layers = NULL, *tmp = NULL; + writer = xmlNewTextWriterFilename (uri, 0); + + xmlTextWriterSetIndent (writer, 1); + xmlTextWriterStartElement (writer, BAD_CAST "pitivi"); + + layers = ges_timeline_get_layers (timeline); + xmlTextWriterStartElement (writer, BAD_CAST "factories"); + xmlTextWriterStartElement (writer, BAD_CAST "sources"); + for (tmp = layers; tmp; tmp = tmp->next) { + + /* 99 is the priority of the background source. */ + if (ges_timeline_layer_get_priority (tmp->data) != 99) { + list = save_sources (tmp->data, writer); + } + } + xmlTextWriterEndElement (writer); + xmlTextWriterEndElement (writer); + save_tracks (timeline, writer, list); + save_timeline_objects (writer, list); + xmlTextWriterEndDocument (writer); + xmlFreeTextWriter (writer); + + g_list_free (layers); + g_list_foreach (list, (GFunc) destroy_all, NULL); + g_list_free (list); + + return TRUE; +} + +static gboolean +load_pitivi_file_from_uri (GESFormatter * self, + GESTimeline * timeline, const gchar * uri) +{ + xmlDocPtr doc; + GESTimelineLayer *layer; + GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv; + + gboolean ret = TRUE; + gint *prio = malloc (sizeof (gint)); + + *prio = 0; + layer = ges_timeline_layer_new (); + g_object_set (layer, "auto-transition", TRUE, NULL); + + g_hash_table_insert (priv->layers_table, prio, layer); + priv->timeline = timeline; + g_object_set (layer, "priority", (gint32) 0, NULL); + + if (!ges_timeline_add_layer (timeline, layer)) { + GST_ERROR ("Couldn't add layer"); + return FALSE; + } + + if (!(doc = create_doc (uri))) { + GST_ERROR ("The xptv file for uri %s was badly formed or did not exist", + uri); + return FALSE; + } + + priv->xpathCtx = xmlXPathNewContext (doc); + + if (!create_tracks (self)) { + GST_ERROR ("Couldn't create tracks"); + ret = FALSE; + goto fail; + } + + priv->source_table = list_sources (self); + + if (!parse_timeline_objects (self)) { + GST_ERROR ("Couldn't find timeline objects markup in the xptv file"); + ret = FALSE; + goto fail; + } + + if (!parse_track_objects (self)) { + GST_ERROR ("Couldn't find track objects markup in the xptv file"); + ret = FALSE; + } + + if (!make_timeline_objects (self)) + ret = FALSE; + +fail: + xmlXPathFreeContext (priv->xpathCtx); + xmlFreeDoc (doc); + return ret; +} + +static void +save_timeline_objects (xmlTextWriterPtr writer, GList * list) +{ + GList *tmp; + gint n_objects, i; + + xmlTextWriterStartElement (writer, BAD_CAST "timeline-objects"); + + for (tmp = list; tmp; tmp = tmp->next) { + + GList *elem; + xmlChar *cast; + + xmlTextWriterStartElement (writer, BAD_CAST "timeline-object"); + elem = tmp->data; + xmlTextWriterStartElement (writer, BAD_CAST "factory-ref"); + cast = g_list_first (elem)->data; + xmlTextWriterWriteAttribute (writer, BAD_CAST "id", BAD_CAST cast); + xmlTextWriterEndElement (writer); + xmlTextWriterStartElement (writer, BAD_CAST "track-object-refs"); + + n_objects = g_list_length (elem) - 4; + for (i = 0; i < n_objects; i++) { + xmlTextWriterStartElement (writer, BAD_CAST "track-object-ref"); + xmlTextWriterWriteAttribute (writer, BAD_CAST "id", + BAD_CAST (gchar *) (g_list_nth (elem, (guint) 4 + i)->data)); + xmlTextWriterEndElement (writer); + } + xmlTextWriterEndElement (writer); + xmlTextWriterEndElement (writer); + } + xmlTextWriterEndElement (writer); +} + +static GList * +save_sources (GESTimelineLayer * layer, xmlTextWriterPtr writer) +{ + GList *objects, *tmp; + GHashTable *source_table; + + GList *source_list = NULL; + int id = 1; + objects = ges_timeline_layer_get_objects (layer); + source_table = + g_hash_table_new_full (g_str_hash, g_int_equal, g_free, g_free); + + for (tmp = objects; tmp; tmp = tmp->next) { + GList *ref_type_list = NULL; + GESTimelineObject *object; + gchar *tfs_uri; + xmlChar *cast; + object = tmp->data; + + if GES_IS_TIMELINE_FILE_SOURCE + (object) { + + tfs_uri = (gchar *) ges_timeline_filesource_get_uri + (GES_TIMELINE_FILE_SOURCE (object)); + + if (!g_hash_table_lookup (source_table, tfs_uri)) { + cast = xmlXPathCastNumberToString (id); + g_hash_table_insert (source_table, g_strdup (tfs_uri), + g_strdup ((gchar *) cast)); + xmlFree (cast); + xmlTextWriterStartElement (writer, BAD_CAST "source"); + xmlTextWriterWriteAttribute (writer, BAD_CAST "filename", + BAD_CAST tfs_uri); + cast = xmlXPathCastNumberToString (id); + xmlTextWriterWriteAttribute (writer, BAD_CAST "id", BAD_CAST cast); + xmlFree (cast); + xmlTextWriterEndElement (writer); + id++; + } + + ref_type_list = + g_list_append (ref_type_list, + g_strdup (g_hash_table_lookup (source_table, tfs_uri))); + ref_type_list = g_list_append (ref_type_list, object); + ref_type_list = g_list_append (ref_type_list, g_strdup ("simple")); + ref_type_list = + g_list_append (ref_type_list, + GINT_TO_POINTER (ges_timeline_layer_get_priority (layer))); + source_list = g_list_append (source_list, g_list_copy (ref_type_list)); + g_list_free (ref_type_list); + } + } + + g_object_unref (G_OBJECT (layer)); + g_list_free (objects); + g_hash_table_destroy (source_table); + return source_list; +} + +static void +save_tracks (GESTimeline * timeline, xmlTextWriterPtr writer, + GList * source_list) +{ + GList *tracks, *tmp; + + gint id = 0; + + xmlTextWriterStartElement (writer, BAD_CAST "timeline"); + xmlTextWriterStartElement (writer, BAD_CAST "tracks"); + tracks = ges_timeline_get_tracks (timeline); + + for (tmp = tracks; tmp; tmp = tmp->next) { + gchar *type, *caps, *res; + GESTrack *track; + GValue v = { 0 }; + track = GES_TRACK (tmp->data); + xmlTextWriterStartElement (writer, BAD_CAST "track"); + xmlTextWriterStartElement (writer, BAD_CAST "stream"); + g_value_init (&v, GES_TYPE_TRACK_TYPE); + g_object_get_property (G_OBJECT (track), "track-type", &v); + type = gst_value_serialize (&v); + caps = gst_caps_to_string (ges_track_get_caps (track)); + xmlTextWriterWriteAttribute (writer, BAD_CAST "caps", BAD_CAST caps); + g_free (caps); + + if (!g_strcmp0 (type, "GES_TRACK_TYPE_AUDIO")) { + xmlTextWriterWriteAttribute (writer, BAD_CAST "type", + BAD_CAST "pitivi.stream.AudioStream"); + xmlTextWriterEndElement (writer); + res = (gchar *) "audio"; + } else { + xmlTextWriterWriteAttribute (writer, BAD_CAST "type", + BAD_CAST "pitivi.stream.VideoStream"); + xmlTextWriterEndElement (writer); + res = (gchar *) "video"; + } + g_free (type); + save_track_objects (writer, source_list, res, &id); + xmlTextWriterEndElement (writer); + } + + g_list_free (tracks); + xmlTextWriterEndElement (writer); +} + +static void +save_track_objects (xmlTextWriterPtr writer, GList * source_list, gchar * res, + gint * id) +{ + GList *tmp, *tck_objs, *tmp_tck; + gchar *bin_desc; + xmlTextWriterStartElement (writer, BAD_CAST "track-objects"); + + for (tmp = source_list; tmp; tmp = tmp->next) { + GList *elem; + GESTimelineObject *object; + guint i, n, j; + + elem = tmp->data; + object = g_list_next (elem)->data; + tck_objs = ges_timeline_object_get_track_objects (object); + + for (tmp_tck = tck_objs; tmp_tck; tmp_tck = tmp_tck->next) { + GParamSpec **properties; + xmlChar *cast; + gchar *prio_str; + + if (!ges_track_object_is_active (tmp_tck->data)) { + continue; + } + + if (((ges_track_object_get_track (tmp_tck->data)->type == + GES_TRACK_TYPE_VIDEO) + && (!g_strcmp0 (res, (gchar *) "video"))) + || ((ges_track_object_get_track (tmp_tck->data)->type == + GES_TRACK_TYPE_AUDIO) + && (!g_strcmp0 (res, (gchar *) "audio")))) { + } else { + continue; + } + + xmlTextWriterStartElement (writer, BAD_CAST "track-object"); + cast = + xmlXPathCastNumberToString (GPOINTER_TO_INT (g_list_nth (elem, + (guint) 3)->data)); + prio_str = g_strconcat ((gchar *) "(int)", (gchar *) cast, NULL); + xmlFree (cast); + xmlTextWriterWriteAttribute (writer, BAD_CAST "priority", + BAD_CAST prio_str); + g_free (prio_str); + properties = + g_object_class_list_properties (G_OBJECT_GET_CLASS (tmp_tck->data), + &n); + + for (i = 0; i < n; i++) { + GParamSpec *p = properties[i]; + GValue v = { 0 }; + gchar *serialized, *concatenated; + + if (!g_strcmp0 (p->name, (gchar *) "duration") || + !g_strcmp0 (p->name, (gchar *) "start") || + !g_strcmp0 (p->name, (gchar *) "in-point")) { + g_value_init (&v, p->value_type); + g_object_get_property (G_OBJECT (tmp_tck->data), p->name, &v); + serialized = gst_value_serialize (&v); + concatenated = g_strconcat ((gchar *) "(gint64)", serialized, NULL); + + if (!g_strcmp0 (p->name, (gchar *) "in-point")) { + xmlTextWriterWriteAttribute (writer, BAD_CAST (gchar *) "in_point", + BAD_CAST concatenated); + } else { + xmlTextWriterWriteAttribute (writer, BAD_CAST p->name, + BAD_CAST concatenated); + } + g_free (concatenated); + g_free (serialized); + } + } + g_free (properties); + cast = xmlXPathCastNumberToString (*id); + xmlTextWriterWriteAttribute (writer, BAD_CAST "id", BAD_CAST cast); + xmlFree (cast); + + if (GES_IS_TRACK_EFFECT (tmp_tck->data)) { + GParamSpec **pspecs, *spec; + gchar *serialized, *concatenated; + guint n_props = 0; + + g_object_get (tmp_tck->data, "bin-description", &bin_desc, NULL); + xmlTextWriterStartElement (writer, BAD_CAST "effect"); + xmlTextWriterStartElement (writer, BAD_CAST "factory"); + xmlTextWriterWriteAttribute (writer, BAD_CAST "name", + BAD_CAST bin_desc); + xmlTextWriterEndElement (writer); + xmlTextWriterStartElement (writer, BAD_CAST "gst-element-properties"); + + pspecs = + ges_track_object_list_children_properties (tmp_tck->data, &n_props); + + j = 0; + + while (j < n_props) { + GValue val = { 0 }; + spec = pspecs[j]; + g_value_init (&val, spec->value_type); + ges_track_object_get_child_property_by_pspec (tmp_tck->data, spec, + &val); + serialized = gst_value_serialize (&val); + if (!g_strcmp0 (spec->name, (gchar *) "preset")) { + concatenated = + g_strconcat ("(GEnum)", + xmlXPathCastNumberToString ((g_value_get_enum (&val))), NULL); + xmlTextWriterWriteAttribute (writer, BAD_CAST spec->name, + BAD_CAST concatenated); + } else { + concatenated = + g_strconcat ("(", g_type_name (spec->value_type), ")", + serialized, NULL); + xmlTextWriterWriteAttribute (writer, BAD_CAST spec->name, + BAD_CAST concatenated); + } + j++; + } + + xmlTextWriterEndElement (writer); + + for (i = 0; i < n_props; i++) { + g_param_spec_unref (pspecs[i]); + } + + g_free (pspecs); + + } else { + xmlTextWriterStartElement (writer, BAD_CAST "factory-ref"); + cast = g_list_first (elem)->data; + xmlTextWriterWriteAttribute (writer, BAD_CAST "id", BAD_CAST cast); + } + xmlTextWriterEndElement (writer); + xmlTextWriterEndElement (writer); + + if (GES_IS_TRACK_EFFECT (tmp_tck->data)) { + elem = g_list_append (elem, xmlXPathCastNumberToString (*id)); + } else { + elem = g_list_insert (elem, xmlXPathCastNumberToString (*id), 4); + } + *id = *id + 1; + } + } + + xmlTextWriterEndElement (writer); +} + +static xmlDocPtr +create_doc (const gchar * uri) +{ + xmlDocPtr doc; + doc = xmlParseFile (uri); + return doc; +} + +static GHashTable * +list_sources (GESFormatter * self) +{ + GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv; + xmlXPathObjectPtr xpathObj; + GHashTable *table, *sources_table; + int size, j; + gchar *id; + xmlNodeSetPtr nodes; + + sources_table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); + xpathObj = xmlXPathEvalExpression ((const xmlChar *) + "/pitivi/factories/sources/source", priv->xpathCtx); + nodes = xpathObj->nodesetval; + size = (nodes) ? nodes->nodeNr : 0; + for (j = 0; j < size; ++j) { + table = get_nodes_infos (nodes->nodeTab[j]); + id = (gchar *) g_hash_table_lookup (table, (gchar *) "id"); + g_hash_table_insert (sources_table, g_strdup (id), table); + } + + xmlXPathFreeObject (xpathObj); + return sources_table; +} + +static gboolean +make_timeline_objects (GESFormatter * self) +{ + GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv; + GHashTable *source_table; + GESTimelineLayer *back_layer; + gint i; + gint *prio = malloc (sizeof (gint)); + + GList *keys = NULL, *tmp = NULL, *ref_list = NULL; + + *prio = 0; + + priv->background = ges_timeline_test_source_new (); + back_layer = ges_timeline_layer_new (); + ges_timeline_layer_set_priority (back_layer, 99); + if (!ges_timeline_add_layer (priv->timeline, back_layer)) { + GST_ERROR ("Couldn't add layer"); + return FALSE; + } + + if (!ges_timeline_layer_add_object (back_layer, + GES_TIMELINE_OBJECT (priv->background))) { + GST_ERROR ("Couldn't add background to the layer"); + return FALSE; + } + + keys = g_hash_table_get_keys (priv->timeline_objects_table); + + for (tmp = keys, i = 1; tmp; tmp = tmp->next, i++) { + if (i == g_list_length (keys)) + priv->parsed = TRUE; + ref_list = + g_hash_table_lookup (priv->timeline_objects_table, (gchar *) tmp->data); + source_table = + g_hash_table_lookup (priv->source_table, (gchar *) tmp->data); + make_source (ref_list, source_table, self); + } + g_hash_table_insert (priv->layers_table, prio, back_layer); + free (prio); + + g_list_free (keys); + return TRUE; +} + +void +make_source (GList * ref_list, GHashTable * source_table, GESFormatter * self) +{ + GHashTable *props_table, *effect_table; + gchar **prio_array; + GESTimelineLayer *layer; + GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv; + + gchar *fac_ref = NULL, *media_type = NULL, *filename = NULL; + GList *tmp = NULL, *keys, *tmp_key; + GESTimelineFileSource *src = NULL; + gint cast_prio = 0; + gint *prio = malloc (sizeof (gint)); + gboolean a_avail = FALSE, v_avail = FALSE, video = FALSE; + + for (tmp = ref_list; tmp; tmp = tmp->next) { + props_table = + g_hash_table_lookup (priv->track_objects_table, (gchar *) tmp->data); + prio_array = + g_strsplit ((gchar *) g_hash_table_lookup (props_table, + (gchar *) "priority"), ")", 0); + cast_prio = (gint) g_ascii_strtod (prio_array[1], NULL); + *prio = cast_prio; + fac_ref = (gchar *) g_hash_table_lookup (props_table, (gchar *) "fac_ref"); + media_type = + (gchar *) g_hash_table_lookup (props_table, (gchar *) "media_type"); + + g_strfreev (prio_array); + + if (!g_strcmp0 (media_type, (gchar *) "pitivi.stream.VideoStream")) + video = TRUE; + else + video = FALSE; + + if (!(layer = g_hash_table_lookup (priv->layers_table, &cast_prio))) { + layer = ges_timeline_layer_new (); + g_object_set (layer, "auto-transition", TRUE, NULL); + ges_timeline_layer_set_priority (layer, cast_prio); + ges_timeline_add_layer (priv->timeline, layer); + g_hash_table_insert (priv->layers_table, prio, layer); + free (prio); + } + + if (g_strcmp0 (fac_ref, (gchar *) "effect") && a_avail && (!video)) { + a_avail = FALSE; + g_signal_connect (src, "track-object-added", + G_CALLBACK (track_object_added_cb), props_table); + + } else if (g_strcmp0 (fac_ref, (gchar *) "effect") && v_avail && (video)) { + v_avail = FALSE; + g_signal_connect (src, "track-object-added", + G_CALLBACK (track_object_added_cb), props_table); + + } else if (g_strcmp0 (fac_ref, (gchar *) "effect")) { + char cCurrentPath[FILENAME_MAX], *path; + + if (a_avail) { + ges_timeline_filesource_set_supported_formats (src, + GES_TRACK_TYPE_VIDEO); + } else if (v_avail) { + ges_timeline_filesource_set_supported_formats (src, + GES_TRACK_TYPE_AUDIO); + } + + filename = + (gchar *) g_hash_table_lookup (source_table, (gchar *) "filename"); + path = GetCurrentDir (cCurrentPath, sizeof (cCurrentPath)); + + if (!g_strcmp0 (filename, (gchar *) "/DJ5r3oNFVeE.flv") || + !g_strcmp0 (filename, (gchar *) "/a1Y73sPHKxw.flv")) { + filename = g_strconcat ("file://", path, filename, NULL); + src = ges_timeline_filesource_new (filename); + g_free (filename); + } else { + src = ges_timeline_filesource_new (filename); + } + + if (!video) { + v_avail = TRUE; + a_avail = FALSE; + } else { + a_avail = TRUE; + v_avail = FALSE; + } + set_properties (G_OBJECT (src), props_table); + ges_timeline_layer_add_object (layer, GES_TIMELINE_OBJECT (src)); + + } else if (!g_strcmp0 (fac_ref, (gchar *) "effect")) { + GESTrackParseLaunchEffect *effect; + gchar *active = (gchar *) + g_hash_table_lookup (props_table, (gchar *) "active"); + + effect = ges_track_parse_launch_effect_new ((gchar *) + g_hash_table_lookup (props_table, (gchar *) "effect_name")); + effect_table = + g_hash_table_lookup (props_table, (gchar *) "effect_props"); + + ges_timeline_object_add_track_object (GES_TIMELINE_OBJECT (src), + GES_TRACK_OBJECT (effect)); + + if (!g_strcmp0 (active, (gchar *) "(bool)False")) + ges_track_object_set_active (GES_TRACK_OBJECT (effect), FALSE); + if (video) + ges_track_add_object (priv->trackv, GES_TRACK_OBJECT (effect)); + else + ges_track_add_object (priv->tracka, GES_TRACK_OBJECT (effect)); + keys = g_hash_table_get_keys (effect_table); + + for (tmp_key = keys; tmp_key; tmp_key = tmp_key->next) { + gchar **value_array = + g_strsplit ((gchar *) g_hash_table_lookup (effect_table, + (gchar *) tmp_key->data), + (gchar *) ")", (gint) 0); + gchar *value = g_ascii_strdown (value_array[1], -1); + + if (!g_strcmp0 (value, (gchar *) "true")) + ges_track_object_set_child_property (GES_TRACK_OBJECT (effect), + (gchar *) tmp_key->data, TRUE, NULL); + else if (!g_strcmp0 (value, (gchar *) "false")) + ges_track_object_set_child_property (GES_TRACK_OBJECT (effect), + (gchar *) tmp_key->data, FALSE, NULL); + else if (!g_strcmp0 (value, (gchar *) "effect")) + continue; + else if (!g_strcmp0 ((gchar *) "(guint", value_array[0]) + || !g_strcmp0 ((gchar *) "(GEnum", value_array[0]) + || !g_strcmp0 ((gchar *) "(gint", value_array[0])) + ges_track_object_set_child_property (GES_TRACK_OBJECT (effect), + (gchar *) tmp_key->data, g_ascii_strtoll (value, NULL, 0), NULL); + else + ges_track_object_set_child_property (GES_TRACK_OBJECT (effect), + (gchar *) tmp_key->data, g_ascii_strtod (value, NULL), NULL); + } + } + } + + if (a_avail) { + ges_timeline_filesource_set_supported_formats (src, GES_TRACK_TYPE_VIDEO); + } else if (v_avail) { + ges_timeline_filesource_set_supported_formats (src, GES_TRACK_TYPE_AUDIO); + } + free (prio); +} + +void +set_properties (GObject * obj, GHashTable * props_table) +{ + gint i; + gchar **prop_array; + gint64 prop_value; + + gchar list[3][10] = { "duration", "in_point", "start" }; + + for (i = 0; i < 3; i++) { + prop_array = + g_strsplit ((gchar *) g_hash_table_lookup (props_table, list[i]), + (gchar *) ")", (gint) 0); + prop_value = g_ascii_strtoll ((gchar *) prop_array[1], NULL, 0); + g_object_set (obj, list[i], prop_value, NULL); + g_strfreev (prop_array); + } +} + +static gboolean +parse_track_objects (GESFormatter * self) +{ + GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv; + xmlXPathObjectPtr xpathObj; + xmlNodeSetPtr nodes; + int size, j; + gchar *id, *fac_ref; + GHashTable *table = NULL, *new_table, *effect_table; + xmlNode *ref_node; + gchar *media_type; + + xpathObj = xmlXPathEvalExpression ((const xmlChar *) + "/pitivi/timeline/tracks/track/track-objects/track-object", + priv->xpathCtx); + + if (xpathObj == NULL) { + xmlXPathFreeObject (xpathObj); + return FALSE; + } + nodes = xpathObj->nodesetval; + size = (nodes) ? nodes->nodeNr : 0; + + for (j = 0; j < size; ++j) { + GHashTable *new_effect_table = NULL; + table = get_nodes_infos (nodes->nodeTab[j]); + id = (gchar *) g_hash_table_lookup (table, (gchar *) "id"); + ref_node = nodes->nodeTab[j]->children->next; + fac_ref = (gchar *) xmlGetProp (ref_node, (xmlChar *) "id"); + new_table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); + + if (!g_strcmp0 ((gchar *) ref_node->name, (gchar *) "effect")) { + fac_ref = (gchar *) "effect"; + ref_node = ref_node->children->next; + new_effect_table = + g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); + g_hash_table_insert (table, g_strdup ((gchar *) "effect_name"), + g_strdup ((gchar *) xmlGetProp (ref_node, (xmlChar *) "name"))); + effect_table = get_nodes_infos (ref_node->next->next); + g_hash_table_foreach (effect_table, (GHFunc) create_new_source_table, + new_effect_table); + } + + g_hash_table_insert (table, g_strdup ((gchar *) "fac_ref"), + g_strdup (fac_ref)); + media_type = + (gchar *) xmlGetProp (nodes->nodeTab[j]->parent->prev->prev, + (xmlChar *) "type"); + g_hash_table_insert (table, g_strdup ((gchar *) "media_type"), + g_strdup (media_type)); + g_hash_table_foreach (table, (GHFunc) create_new_source_table, new_table); + if (new_effect_table) { + g_hash_table_insert (new_table, (gchar *) "effect_props", + new_effect_table); + } + g_hash_table_insert (priv->track_objects_table, g_strdup (id), new_table); + xmlFree (media_type); + if (g_strcmp0 (fac_ref, (gchar *) "effect")) { + xmlFree (fac_ref); + } + g_hash_table_foreach (table, (GHFunc) destroyer, NULL); + g_hash_table_destroy (table); + } + + xmlXPathFreeObject (xpathObj); + return TRUE; +} + +static gboolean +parse_timeline_objects (GESFormatter * self) +{ + GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv; + xmlXPathObjectPtr xpathObj; + xmlNodeSetPtr nodes; + int size, j; + gchar *id, *ref; + xmlChar *refxml; + + GList *ref_list = NULL, *tmp_list = NULL, *tmp = NULL; + xmlNode *cur_node = NULL; + xpathObj = xmlXPathEvalExpression ((const xmlChar *) + "/pitivi/timeline/timeline-objects/timeline-object/factory-ref", + priv->xpathCtx); + + if (xpathObj == NULL) { + xmlXPathFreeObject (xpathObj); + return FALSE; + } + + nodes = xpathObj->nodesetval; + size = (nodes) ? nodes->nodeNr : 0; + + for (j = 0; j < size; ++j) { + cur_node = nodes->nodeTab[j]; + id = (gchar *) xmlGetProp (cur_node, (xmlChar *) "id"); + cur_node = cur_node->next->next->children->next; + ref_list = NULL; + for (cur_node = cur_node; cur_node; cur_node = cur_node->next->next) { + + refxml = xmlGetProp (cur_node, (xmlChar *) "id"); + ref = (gchar *) refxml; + ref_list = g_list_append (ref_list, g_strdup (ref)); + xmlFree (refxml); + } + tmp_list = g_hash_table_lookup (priv->timeline_objects_table, id); + if (tmp_list != NULL) { + for (tmp = tmp_list; tmp; tmp = tmp->next) { + ref_list = g_list_append (ref_list, tmp->data); + } + } + g_hash_table_insert (priv->timeline_objects_table, g_strdup (id), + g_list_copy (ref_list)); + xmlFree (id); + g_list_free (ref_list); + g_list_free (tmp_list); + g_list_free (tmp); + } + xmlXPathFreeObject (xpathObj); + return TRUE; +} + +static void +create_new_source_table (gchar * key, gchar * value, GHashTable * table) +{ + g_hash_table_insert (table, g_strdup (key), g_strdup (value)); +} + +static gboolean +create_tracks (GESFormatter * self) +{ + GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv; + GList *tracks = NULL; + + tracks = ges_timeline_get_tracks (priv->timeline); + if (g_list_length (tracks)) { + GList *tmp = NULL; + GESTrack *track; + for (tmp = tracks; tmp; tmp = tmp->next) { + track = tmp->data; + if (track->type == GES_TRACK_TYPE_AUDIO) { + priv->tracka = track; + } else { + priv->trackv = track; + } + } + g_list_free (tracks); + return TRUE; + } + + priv->tracka = ges_track_audio_raw_new (); + priv->trackv = ges_track_video_raw_new (); + + if (!ges_timeline_add_track (priv->timeline, priv->trackv)) { + return FALSE; + } + + if (!ges_timeline_add_track (priv->timeline, priv->tracka)) { + return FALSE; + } + + return TRUE; +} + +static GHashTable * +get_nodes_infos (xmlNodePtr node) +{ + xmlAttr *cur_attr; + GHashTable *props_table; + gchar *name, *value; + + props_table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); + + for (cur_attr = node->properties; cur_attr; cur_attr = cur_attr->next) { + name = (gchar *) cur_attr->name; + value = (gchar *) xmlGetProp (node, cur_attr->name); + g_hash_table_insert (props_table, g_strdup (name), g_strdup (value)); + xmlFree (value); + } + + return props_table; +} + +static void +destroy_all (GList * list) +{ + g_free (g_list_nth (list, (guint) 2)->data); + g_object_unref (G_OBJECT (g_list_nth (list, (guint) 1)->data)); + g_free (g_list_nth (list, (guint) 3)->data); + + if (g_list_length (list) == 6) { + g_free (g_list_nth (list, (guint) 5)->data); + } + + g_free (g_list_nth (list, (guint) 0)->data); + g_list_free (list); +} + +static void +track_object_added_cb (GESTimelineObject * object, + GESTrackObject * track_object, GHashTable * props_table) +{ + gchar *media_type = NULL; + GList *tck_objs = NULL, *tmp = NULL; + GESTrack *object_track; + gint64 start, duration; + gboolean has_effect = FALSE; + gint type = 0; + tck_objs = ges_timeline_object_get_track_objects (object); + media_type = + (gchar *) g_hash_table_lookup (props_table, (gchar *) "media_type"); + + for (tmp = tck_objs; tmp; tmp = tmp->next) { + object_track = ges_track_object_get_track (tmp->data); + if (GES_IS_TRACK_PARSE_LAUNCH_EFFECT (tmp->data)) { + has_effect = TRUE; + continue; + } + if ((!g_strcmp0 (media_type, "pitivi.stream.VideoStream") + && object_track->type == GES_TRACK_TYPE_VIDEO) + || (!g_strcmp0 (media_type, "pitivi.stream.AudioStream") + && object_track->type == GES_TRACK_TYPE_AUDIO)) { + ges_track_object_set_locked (tmp->data, FALSE); + set_properties (G_OBJECT (tmp->data), props_table); + ges_track_object_set_locked (tmp->data, TRUE); + type = object_track->type; + g_object_get (tmp->data, "start", &start, "duration", &duration, NULL); + } + } + + if (has_effect) { + tck_objs = ges_timeline_object_get_track_objects (object); + for (tmp = tck_objs; tmp; tmp = tmp->next) { + object_track = ges_track_object_get_track (tmp->data); + if (GES_IS_TRACK_PARSE_LAUNCH_EFFECT (tmp->data) + && (type == object_track->type)) { + ges_track_object_set_locked (tmp->data, FALSE); + g_object_set (tmp->data, "start", start, "duration", duration, NULL); + ges_track_object_set_locked (tmp->data, TRUE); + } + } + } +} + +void +ultimate_table_destroyer (gpointer data, gpointer data2, void *unused) +{ + g_free (data); + g_hash_table_foreach (data2, (GHFunc) destroyer, NULL); + g_hash_table_destroy (data2); +} + +void +layers_table_destroyer (gpointer data, gpointer data2, void *unused) +{ + g_object_unref (data2); + g_free (data); +} + +void +list_table_destroyer (gpointer data, gpointer data2, void *unused) +{ + g_list_foreach (data2, (GFunc) g_free, NULL); + g_list_free (data2); + g_free (data); +} + +void +destroyer (gpointer data, gpointer data2, void *unused) +{ + if (!g_strcmp0 ((gchar *) data, (gchar *) "private")) { + } else if (!g_strcmp0 ((gchar *) data, (gchar *) "effect_props")) { + g_hash_table_foreach (data2, (GHFunc) destroyer, NULL); + g_hash_table_destroy (data2); + } else { + g_free (data2); + g_free (data); + } +} + +GESPitiviFormatter * +ges_pitivi_formatter_new (void) +{ + return g_object_new (GES_TYPE_PITIVI_FORMATTER, NULL); +} diff --git a/ges/ges-pitivi-formatter.h b/ges/ges-pitivi-formatter.h new file mode 100644 index 0000000..8965316 --- /dev/null +++ b/ges/ges-pitivi-formatter.h @@ -0,0 +1,61 @@ +#ifndef _GES_PITIVI_FORMATTER +#define _GES_PITIVI_FORMATTER +#include +#include +#include +#include +#include +#include "libxml/encoding.h" +#include "libxml/xmlwriter.h" + + +#define GES_TYPE_PITIVI_FORMATTER ges_pitivi_formatter_get_type() + +#define GES_PITIVI_FORMATTER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_PITIVI_FORMATTER, GESPitiviFormatter)) + +#define GES_PITIVI_FORMATTER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_PITIVI_FORMATTER, GESPitiviFormatterClass)) + +#define GES_IS_PITIVI_FORMATTER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_PITIVI_FORMATTER)) + +#define GES_IS_PITIVI_FORMATTER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_PITIVI_FORMATTER)) + +#define GES_PITIVI_FORMATTER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_PITIVI_FORMATTER, GESPitiviFormatterClass)) + +typedef struct _GESPitiviFormatterPrivate GESPitiviFormatterPrivate; + +/** + * GESPitiviFormatter: + * + * Serializes a #GESTimeline to a file using + */ + +struct _GESPitiviFormatter { + GESFormatter parent; + + /*< private >*/ + GESPitiviFormatterPrivate *priv; + + /* Padding for API extension */ + gpointer _ges_reserved[GES_PADDING]; +}; + +struct _GESPitiviFormatterClass { + /*< private >*/ + GESFormatterClass parent_class; + + /* Padding for API extension */ + gpointer _ges_reserved[GES_PADDING]; +}; + +GType ges_pitivi_formatter_get_type (void); + +GESPitiviFormatter *ges_pitivi_formatter_new (void); + + + +#endif /* _GES_PITIVI_FORMATTER */ diff --git a/ges/ges-types.h b/ges/ges-types.h index b16f215..455c457 100644 --- a/ges/ges-types.h +++ b/ges/ges-types.h @@ -134,5 +134,7 @@ typedef struct _GESFormatterClass GESFormatterClass; typedef struct _GESKeyfileFormatter GESKeyfileFormatter; typedef struct _GESKeyfileFormatterClass GESKeyfileFormatterClass; +typedef struct _GESPitiviFormatter GESPitiviFormatter; +typedef struct _GESPitiviFormatterClass GESPitiviFormatterClass; #endif /* __GES_TYPES_H__ */ diff --git a/ges/ges.h b/ges/ges.h index 8584d0b..123da7a 100644 --- a/ges/ges.h +++ b/ges/ges.h @@ -20,7 +20,6 @@ #ifndef __GES_H__ #define __GES_H__ - #include #include @@ -63,10 +62,9 @@ #include #include #include - #include #include - +#include #include G_BEGIN_DECLS -- 2.7.4