ges: Implement a Pitivi Formatter
authorMathieu Duponchelle <seeed@laposte.net>
Sun, 28 Aug 2011 01:55:46 +0000 (03:55 +0200)
committerThibault Saunier <thibault.saunier@collabora.com>
Wed, 11 Jan 2012 14:56:14 +0000 (11:56 -0300)
API: ges_pitivi_formatter_new

ges/Makefile.am
ges/ges-pitivi-formatter.c [new file with mode: 0644]
ges/ges-pitivi-formatter.h [new file with mode: 0644]
ges/ges-types.h
ges/ges.h

index 55b082c..a38f726 100644 (file)
@@ -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 (file)
index 0000000..590491e
--- /dev/null
@@ -0,0 +1,1018 @@
+#include <ges/ges.h>
+#include <unistd.h>
+#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 (file)
index 0000000..8965316
--- /dev/null
@@ -0,0 +1,61 @@
+#ifndef _GES_PITIVI_FORMATTER
+#define _GES_PITIVI_FORMATTER
+#include <libxml/xmlreader.h>
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+#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 */
index b16f215..455c457 100644 (file)
@@ -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__ */
index 8584d0b..123da7a 100644 (file)
--- a/ges/ges.h
+++ b/ges/ges.h
@@ -20,7 +20,6 @@
 
 #ifndef __GES_H__
 #define __GES_H__
-
 #include <glib.h>
 #include <gst/gst.h>
 
 #include <ges/ges-track-audio-transition.h>
 #include <ges/ges-track-effect.h>
 #include <ges/ges-track-parse-launch-effect.h>
-
 #include <ges/ges-formatter.h>
 #include <ges/ges-keyfile-formatter.h>
-
+#include <ges/ges-pitivi-formatter.h>
 #include <ges/ges-utils.h>
 
 G_BEGIN_DECLS