mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 00:36:51 +00:00
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:
parent
ff274dee2e
commit
241e809a81
7 changed files with 100 additions and 42 deletions
|
@ -1035,6 +1035,7 @@ GES_META_FORMATTER_VERSION
|
|||
GES_META_FORMATTER_RANK
|
||||
GES_META_DESCRIPTION
|
||||
|
||||
GES_META_FORMAT_VERSION
|
||||
|
||||
<SUBSECTION Standard>
|
||||
GESMetaContainerInterface
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<project metadatas='metadatas, name=(string)"Example\ project";'>
|
||||
<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>
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue