ges: Enhance xges format versioning

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
This commit is contained in:
Thibault Saunier 2015-05-18 21:24:25 +02:00
parent ff274dee2e
commit 241e809a81
7 changed files with 100 additions and 42 deletions

View file

@ -1035,6 +1035,7 @@ GES_META_FORMATTER_VERSION
GES_META_FORMATTER_RANK
GES_META_DESCRIPTION
GES_META_FORMAT_VERSION
<SUBSECTION Standard>
GESMetaContainerInterface

View 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;
}

View 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;

View 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

View 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;

View 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>

View 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);