ges: Enhance xges format versioning
authorThibault Saunier <tsaunier@gnome.org>
Mon, 18 May 2015 19:24:25 +0000 (21:24 +0200)
committerThibault Saunier <tsaunier@gnome.org>
Mon, 18 May 2015 19:27:48 +0000 (21:27 +0200)
Summary:
Handle the fact that some new features can be added and that means
generated files will not be fully understandable by older versions of
the formatter.

Make sure that we set the format version to 0.2 when we serialize the
GstEncodingProfile.enabled property.

Add some tests around that.

+ Fix a minor bug in the test-utils
+ Add a meta on the projects to tell in what format version a project
  has been serialized/parsed back

API:
  GES_META_FORMAT_VERSION

Depends on D178

Reviewers: Mathieu_Du

Differential Revision: http://phabricator.freedesktop.org/D184

docs/libs/ges-sections.txt
ges/ges-formatter.c
ges/ges-meta-container.h
ges/ges-xml-formatter.c
tests/check/ges/project.c
tests/check/ges/test-project.xges
tests/check/ges/test-utils.c

index 6283fbe..f6e728f 100644 (file)
@@ -1035,6 +1035,7 @@ GES_META_FORMATTER_VERSION
 GES_META_FORMATTER_RANK
 GES_META_DESCRIPTION
 
+GES_META_FORMAT_VERSION
 
 <SUBSECTION Standard>
 GESMetaContainerInterface
index a053901..07fa4eb 100644 (file)
@@ -92,6 +92,8 @@ _register_metas (GESExtractableInterface * iface, GObjectClass * class,
       GES_META_FORMATTER_VERSION, fclass->version);
   ges_meta_container_register_meta_uint (container, GES_META_READABLE,
       GES_META_FORMATTER_RANK, fclass->rank);
+  ges_meta_container_register_meta_string (container, GES_META_READ_WRITE,
+      GES_META_FORMAT_VERSION, NULL);
 
   return TRUE;
 }
index 200dab5..bae3a04 100644 (file)
@@ -102,6 +102,13 @@ G_BEGIN_DECLS
  */
 #define GES_META_VOLUME_DEFAULT                       1.0
 
+/**
+ * GES_META_FORMAT_VERSION:
+ *
+ * The version of the format in which a project is serialized
+ */
+#define GES_META_FORMAT_VERSION                       "format-version"
+
 typedef struct _GESMetaContainer          GESMetaContainer;
 typedef struct _GESMetaContainerInterface GESMetaContainerInterface;
 
index 89d5173..9eded2e 100644 (file)
@@ -32,8 +32,8 @@
 G_DEFINE_TYPE (GESXmlFormatter, ges_xml_formatter, GES_TYPE_BASE_XML_FORMATTER);
 
 #define API_VERSION 0
-#define MINOR_VERSION 1
-#define VERSION 0.1
+#define MINOR_VERSION 2
+#define VERSION 0.2
 
 #define COLLECT_STR_OPT (G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL)
 
@@ -49,6 +49,8 @@ struct _GESXmlFormatterPrivate
   GHashTable *element_id;
 
   guint nbelements;
+
+  guint min_version;
 };
 
 static inline void
@@ -56,8 +58,8 @@ _parse_ges_element (GMarkupParseContext * context, const gchar * element_name,
     const gchar ** attribute_names, const gchar ** attribute_values,
     GESXmlFormatter * self, GError ** error)
 {
+  guint api_version;
   const gchar *version, *properties;
-  guint api_version, min_version;
 
   gchar **split_version = NULL;
 
@@ -84,8 +86,8 @@ _parse_ges_element (GMarkupParseContext * context, const gchar * element_name,
   if (errno || api_version != API_VERSION)
     goto stroull_failed;
 
-  min_version = g_ascii_strtoull (split_version[1], NULL, 10);
-  if (min_version > MINOR_VERSION)
+  self->priv->min_version = g_ascii_strtoull (split_version[1], NULL, 10);
+  if (self->priv->min_version > MINOR_VERSION)
     goto failed;
 
   _GET_PRIV (self)->ges_opened = TRUE;
@@ -780,6 +782,15 @@ _parse_element_end (GMarkupParseContext * context,
     const gchar * element_name, gpointer self, GError ** error)
 {
   /*GESXmlFormatterPrivate *priv = _GET_PRIV (self); */
+  if (g_strcmp0 (element_name, "ges") == 0 && GES_FORMATTER (self)->project) {
+    gchar *version = g_strdup_printf ("%d.%d",
+        API_VERSION, GES_XML_FORMATTER (self)->priv->min_version);
+
+    ges_meta_container_set_string (GES_META_CONTAINER (GES_FORMATTER
+            (self)->project), GES_META_FORMAT_VERSION, version);
+
+    g_free (version);
+  }
 }
 
 static void
@@ -1272,8 +1283,8 @@ _save_timeline (GESXmlFormatter * self, GString * str, GESTimeline * timeline)
 }
 
 static void
-_save_stream_profiles (GString * str, GstEncodingProfile * sprof,
-    const gchar * profilename, guint id)
+_save_stream_profiles (GESXmlFormatter * self, GString * str,
+    GstEncodingProfile * sprof, const gchar * profilename, guint id)
 {
   gchar *tmpc;
   GstCaps *tmpcaps;
@@ -1282,10 +1293,15 @@ _save_stream_profiles (GString * str, GstEncodingProfile * sprof,
   append_escaped (str,
       g_markup_printf_escaped
       ("        <stream-profile parent='%s' id='%d' type='%s' "
-          "presence='%d' enabled='%d' ", profilename, id,
+          "presence='%d' ", profilename, id,
           gst_encoding_profile_get_type_nick (sprof),
-          gst_encoding_profile_get_presence (sprof),
-          gst_encoding_profile_is_enabled (sprof)));
+          gst_encoding_profile_get_presence (sprof)));
+
+  if (!gst_encoding_profile_is_enabled (sprof)) {
+    append_escaped (str, g_strdup ("enabled='0' "));
+
+    self->priv->min_version = MAX (self->priv->min_version, 2);
+  }
 
   tmpcaps = gst_encoding_profile_get_format (sprof);
   if (tmpcaps) {
@@ -1334,7 +1350,8 @@ _save_stream_profiles (GString * str, GstEncodingProfile * sprof,
 }
 
 static inline void
-_save_encoding_profiles (GString * str, GESProject * project)
+_save_encoding_profiles (GESXmlFormatter * self, GString * str,
+    GESProject * project)
 {
   GstCaps *profformat;
   const gchar *profname, *profdesc, *profpreset, *proftype, *profpresetname;
@@ -1382,7 +1399,7 @@ _save_encoding_profiles (GString * str, GESProject * project)
       for (tmp2 = gst_encoding_container_profile_get_profiles (container_prof);
           tmp2; tmp2 = tmp2->next, i++) {
         GstEncodingProfile *sprof = (GstEncodingProfile *) tmp2->data;
-        _save_stream_profiles (str, sprof, profname, i);
+        _save_stream_profiles (self, str, sprof, profname, i);
       }
     }
     append_escaped (str,
@@ -1396,16 +1413,17 @@ _save (GESFormatter * formatter, GESTimeline * timeline, GError ** error)
   GString *str;
   GESProject *project;
 
+  gchar *projstr = NULL, *version;
   gchar *properties = NULL, *metas = NULL;
   GESXmlFormatterPrivate *priv;
 
 
   priv = _GET_PRIV (formatter);
+
+  priv->min_version = 1;
   project = formatter->project;
   str = priv->str = g_string_new (NULL);
 
-  g_string_append_printf (str, "<ges version='%i.%i'>\n", API_VERSION,
-      MINOR_VERSION);
   properties = _serialize_properties (G_OBJECT (project), NULL);
   metas = ges_meta_container_metas_to_string (GES_META_CONTAINER (project));
   append_escaped (str,
@@ -1415,7 +1433,7 @@ _save (GESFormatter * formatter, GESTimeline * timeline, GError ** error)
   g_free (metas);
 
   g_string_append (str, "    <encoding-profiles>\n");
-  _save_encoding_profiles (str, project);
+  _save_encoding_profiles (GES_XML_FORMATTER (formatter), str, project);
   g_string_append (str, "    </encoding-profiles>\n");
 
   g_string_append (str, "    <ressources>\n");
@@ -1425,6 +1443,22 @@ _save (GESFormatter * formatter, GESTimeline * timeline, GError ** error)
   _save_timeline (GES_XML_FORMATTER (formatter), str, timeline);
   g_string_append (str, "</project>\n</ges>");
 
+  projstr = g_strdup_printf ("<ges version='%i.%i'>\n", API_VERSION,
+      priv->min_version);
+  g_string_prepend (str, projstr);
+  g_free (projstr);
+
+  ges_meta_container_set_int (GES_META_CONTAINER (project),
+      GES_META_FORMAT_VERSION, priv->min_version);
+
+  version = g_strdup_printf ("%d.%d", API_VERSION,
+      GES_XML_FORMATTER (formatter)->priv->min_version);
+
+  ges_meta_container_set_string (GES_META_CONTAINER (project),
+      GES_META_FORMAT_VERSION, version);
+
+  g_free (version);
+
   priv->str = NULL;
 
   return str;
@@ -1457,6 +1491,7 @@ ges_xml_formatter_init (GESXmlFormatter * self)
   priv->element_id = g_hash_table_new (g_direct_hash, g_direct_equal);
 
   self->priv = priv;
+  self->priv->min_version = 1;
 }
 
 static void
index 13f8df7..0cb9c60 100644 (file)
@@ -484,63 +484,77 @@ GST_END_TEST;
 GST_START_TEST (test_project_load_xges)
 {
   gboolean saved;
-  GESProject *project;
+  GESProject *loaded_project, *saved_project;
   GESTimeline *timeline;
   GESAsset *formatter_asset;
   gchar *uri = ges_test_file_uri ("test-project.xges");
 
-  project = ges_project_new (uri);
+  loaded_project = ges_project_new (uri);
   mainloop = g_main_loop_new (NULL, FALSE);
-  fail_unless (GES_IS_PROJECT (project));
+  fail_unless (GES_IS_PROJECT (loaded_project));
 
   /* Connect the signals */
-  g_signal_connect (project, "asset-added", (GCallback) asset_added_cb, NULL);
-  g_signal_connect (project, "loaded", (GCallback) project_loaded_cb, mainloop);
+  g_signal_connect (loaded_project, "asset-added", (GCallback) asset_added_cb,
+      NULL);
+  g_signal_connect (loaded_project, "loaded", (GCallback) project_loaded_cb,
+      mainloop);
 
   /* Make sure we update the project's dummy URL to some actual URL */
-  g_signal_connect (project, "missing-uri", (GCallback) _set_new_uri, NULL);
+  g_signal_connect (loaded_project, "missing-uri", (GCallback) _set_new_uri,
+      NULL);
 
   /* Now extract a timeline from it */
   GST_LOG ("Loading project");
-  timeline = GES_TIMELINE (ges_asset_extract (GES_ASSET (project), NULL));
+  timeline =
+      GES_TIMELINE (ges_asset_extract (GES_ASSET (loaded_project), NULL));
   fail_unless (GES_IS_TIMELINE (timeline));
-  assert_equals_int (g_list_length (ges_project_get_loading_assets (project)),
-      1);
+  assert_equals_int (g_list_length (ges_project_get_loading_assets
+          (loaded_project)), 1);
 
   g_main_loop_run (mainloop);
   GST_LOG ("Test first loading");
-  _test_project (project, timeline);
+  _test_project (loaded_project, timeline);
   g_free (uri);
 
   uri = ges_test_get_tmp_uri ("test-project_TMP.xges");
   formatter_asset = ges_asset_request (GES_TYPE_FORMATTER, "ges", NULL);
   saved =
-      ges_project_save (project, timeline, uri, formatter_asset, TRUE, NULL);
+      ges_project_save (loaded_project, timeline, uri, formatter_asset, TRUE,
+      NULL);
   fail_unless (saved);
   gst_object_unref (timeline);
-  gst_object_unref (project);
 
-  project = ges_project_new (uri);
-  ASSERT_OBJECT_REFCOUNT (project, "Our + cache", 2);
-  g_signal_connect (project, "asset-added", (GCallback) asset_added_cb, NULL);
-  g_signal_connect (project, "loaded", (GCallback) project_loaded_cb, mainloop);
+  saved_project = ges_project_new (uri);
+  ASSERT_OBJECT_REFCOUNT (saved_project, "Our + cache", 2);
+  g_signal_connect (saved_project, "asset-added", (GCallback) asset_added_cb,
+      NULL);
+  g_signal_connect (saved_project, "loaded", (GCallback) project_loaded_cb,
+      mainloop);
 
   GST_LOG ("Loading saved project");
-  timeline = GES_TIMELINE (ges_asset_extract (GES_ASSET (project), NULL));
+  timeline = GES_TIMELINE (ges_asset_extract (GES_ASSET (saved_project), NULL));
   fail_unless (GES_IS_TIMELINE (timeline));
   g_main_loop_run (mainloop);
-  _test_project (project, timeline);
+  _test_project (saved_project, timeline);
+
+  fail_unless (ges_meta_container_get_string (GES_META_CONTAINER
+          (loaded_project), GES_META_FORMAT_VERSION));
+  fail_unless_equals_string (ges_meta_container_get_string (GES_META_CONTAINER
+          (loaded_project), GES_META_FORMAT_VERSION),
+      ges_meta_container_get_string (GES_META_CONTAINER (loaded_project),
+          GES_META_FORMAT_VERSION));
   gst_object_unref (timeline);
-  gst_object_unref (project);
+  gst_object_unref (saved_project);
+  gst_object_unref (loaded_project);
   g_free (uri);
 
-  ASSERT_OBJECT_REFCOUNT (project, "Still 1 ref for asset cache", 1);
+  ASSERT_OBJECT_REFCOUNT (saved_project, "Still 1 ref for asset cache", 1);
 
   g_main_loop_unref (mainloop);
-  g_signal_handlers_disconnect_by_func (project, (GCallback) project_loaded_cb,
-      mainloop);
-  g_signal_handlers_disconnect_by_func (project, (GCallback) asset_added_cb,
-      NULL);
+  g_signal_handlers_disconnect_by_func (saved_project,
+      (GCallback) project_loaded_cb, mainloop);
+  g_signal_handlers_disconnect_by_func (saved_project,
+      (GCallback) asset_added_cb, NULL);
 }
 
 GST_END_TEST;
index 2569b01..acc71ed 100644 (file)
@@ -2,7 +2,7 @@
   <project metadatas='metadatas, name=(string)&quot;Example\ project&quot;;'>
     <encoding-profiles>
       <encoding-profile name='first_profile' description='(null)' type='container' format='application/ogg'>
-        <stream-profile parent='first_profile' id='0' type='video' presence='0' format='video/x-h264' pass='0' variableframerate='0' />
+        <stream-profile parent='first_profile' enabled='0' id='0' type='video' presence='0' format='video/x-h264' pass='0' variableframerate='0' />
         <stream-profile parent='first_profile' id='1' type='audio' presence='0' format='audio/x-aac' />
       </encoding-profile>
     </encoding-profiles>
index 106e877..86a3bfd 100644 (file)
@@ -283,8 +283,7 @@ ges_test_get_tmp_uri (const gchar * filename)
 {
   gchar *location, *uri;
 
-  location = g_build_filename (g_get_tmp_dir (),
-      "test-keyframes-save.xges", NULL);
+  location = g_build_filename (g_get_tmp_dir (), filename, NULL);
 
   uri = g_strconcat ("file://", location, NULL);
   g_free (location);