formatter: Fix saving/loading project with clip speed rate control

We need to ensure that clips duration is set after time effects are
added and we now need to serialize effects inpoints and max duration.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-editing-services/-/merge_requests/177>
This commit is contained in:
Thibault Saunier 2020-05-21 17:22:18 -04:00 committed by Henry Wilkes
parent e142f49177
commit db5c62ad4c
5 changed files with 56 additions and 16 deletions

View file

@ -94,6 +94,7 @@ struct _GESBaseXmlFormatterPrivate
GESTrackElement *current_track_element; GESTrackElement *current_track_element;
GESClip *current_clip; GESClip *current_clip;
GstClockTime current_clip_duration;
gboolean timeline_auto_transition; gboolean timeline_auto_transition;
@ -416,6 +417,7 @@ ges_base_xml_formatter_init (GESBaseXmlFormatter * self)
g_direct_equal, NULL, (GDestroyNotify) _free_layer_entry); g_direct_equal, NULL, (GDestroyNotify) _free_layer_entry);
priv->current_track_element = NULL; priv->current_track_element = NULL;
priv->current_clip = NULL; priv->current_clip = NULL;
priv->current_clip_duration = GST_CLOCK_TIME_NONE;
priv->timeline_auto_transition = FALSE; priv->timeline_auto_transition = FALSE;
} }
@ -636,8 +638,16 @@ _add_track_element (GESFormatter * self, GESClip * clip,
(GstStructureForeachFunc) _set_child_property, trackelement); (GstStructureForeachFunc) _set_child_property, trackelement);
if (properties) { if (properties) {
gboolean has_internal_source;
/* We do not serialize the priority anymore, and we should never have. */ /* We do not serialize the priority anymore, and we should never have. */
gst_structure_remove_field (properties, "priority"); gst_structure_remove_field (properties, "priority");
/* Ensure that has-internal-source is set before inpoint as otherwise
* the inpoint will be ignored */
if (gst_structure_get_boolean (properties, "has-internal-source",
&has_internal_source) && has_internal_source)
g_object_set (trackelement, "has-internal-source", has_internal_source,
NULL);
gst_structure_foreach (properties, gst_structure_foreach (properties,
(GstStructureForeachFunc) set_property_foreach, trackelement); (GstStructureForeachFunc) set_property_foreach, trackelement);
} }
@ -915,6 +925,7 @@ ges_base_xml_formatter_add_clip (GESBaseXmlFormatter * self,
if (!nclip) if (!nclip)
return; return;
priv->current_clip_duration = duration;
priv->current_clip = nclip; priv->current_clip = nclip;
} }
@ -1335,3 +1346,24 @@ ges_base_xml_formatter_last_group_add_child (GESBaseXmlFormatter * self,
GST_DEBUG_OBJECT (self, "Adding %s to %s", child_id, GST_DEBUG_OBJECT (self, "Adding %s to %s", child_id,
GES_TIMELINE_ELEMENT_NAME (((PendingGroup *) priv->groups->data)->group)); GES_TIMELINE_ELEMENT_NAME (((PendingGroup *) priv->groups->data)->group));
} }
void
ges_base_xml_formatter_end_current_clip (GESBaseXmlFormatter * self)
{
GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
if (priv->state != STATE_LOADING_CLIPS) {
GST_DEBUG_OBJECT (self, "Not ending clip in %s state.",
loading_state_name (priv->state));
return;
}
g_return_if_fail (priv->current_clip);
if (_DURATION (priv->current_clip) != priv->current_clip_duration)
_set_duration0 (GES_TIMELINE_ELEMENT (priv->current_clip),
priv->current_clip_duration);
priv->current_clip = NULL;
priv->current_clip_duration = GST_CLOCK_TIME_NONE;
}

View file

@ -377,7 +377,10 @@ G_GNUC_INTERNAL void ges_base_xml_formatter_set_timeline_properties(GESBaseXmlFo
GESTimeline *timeline, GESTimeline *timeline,
const gchar *properties, const gchar *properties,
const gchar *metadatas); const gchar *metadatas);
G_GNUC_INTERNAL void ges_xml_formatter_deinit (void);
G_GNUC_INTERNAL void ges_base_xml_formatter_end_current_clip (GESBaseXmlFormatter *self);
G_GNUC_INTERNAL void ges_xml_formatter_deinit (void);
G_GNUC_INTERNAL gboolean set_property_foreach (GQuark field_id, G_GNUC_INTERNAL gboolean set_property_foreach (GQuark field_id,
const GValue * value, const GValue * value,

View file

@ -1021,6 +1021,9 @@ _parse_element_end (GMarkupParseContext * context,
if (!priv->subproject_depth) { if (!priv->subproject_depth) {
g_clear_pointer (&priv->subproject, g_free); g_clear_pointer (&priv->subproject, g_free);
} }
} else if (!g_strcmp0 (element_name, "clip")) {
if (!priv->subproject)
ges_base_xml_formatter_end_current_clip (GES_BASE_XML_FORMATTER (self));
} }
} }
@ -1553,8 +1556,7 @@ _save_effect (GString * str, guint clip_id, GESTrackElement * trackelement,
g_list_free_full (tracks, gst_object_unref); g_list_free_full (tracks, gst_object_unref);
properties = _serialize_properties (G_OBJECT (trackelement), NULL, "start", properties = _serialize_properties (G_OBJECT (trackelement), NULL, "start",
"in-point", "duration", "locked", "max-duration", "name", "priority", "duration", "locked", "name", "priority", NULL);
NULL);
metas = metas =
ges_meta_container_metas_to_string (GES_META_CONTAINER (trackelement)); ges_meta_container_metas_to_string (GES_META_CONTAINER (trackelement));
extractable_id = ges_extractable_get_id (GES_EXTRACTABLE (trackelement)); extractable_id = ges_extractable_get_id (GES_EXTRACTABLE (trackelement));

View file

@ -213,11 +213,11 @@ class GESSimpleTimelineTest(GESTest):
return clip return clip
def append_clip(self, layer=0, asset_type=GES.TestClip): def append_clip(self, layer=0, asset_type=GES.TestClip, asset_id=None):
while len(self.timeline.get_layers()) < layer + 1: while len(self.timeline.get_layers()) < layer + 1:
self.timeline.append_layer() self.timeline.append_layer()
layer = self.timeline.get_layers()[layer] layer = self.timeline.get_layers()[layer]
clip = GES.Asset.request(asset_type, None).extract() clip = GES.Asset.request(asset_type, asset_id).extract()
clip.props.start = layer.get_duration() clip.props.start = layer.get_duration()
clip.props.duration = 10 clip.props.duration = 10
self.assertTrue(layer.add_clip(clip)) self.assertTrue(layer.add_clip(clip))
@ -231,16 +231,16 @@ class GESSimpleTimelineTest(GESTest):
and not GObject.type_is_a(p.value_type, GObject.Object)] and not GObject.type_is_a(p.value_type, GObject.Object)]
for p in props: for p in props:
pname = p.name pname = p.name
v0 = GObject.Value() refval = GObject.Value()
v0.init(p.value_type) refval.init(p.value_type)
v0.set_value(ref.get_property(pname)) refval.set_value(ref.get_property(pname))
v1 = GObject.Value() value = GObject.Value()
v1.init(p.value_type) value.init(p.value_type)
v1.set_value(element.get_property(pname)) value.set_value(element.get_property(pname))
self.assertTrue(Gst.value_compare(v0, v1) == Gst.VALUE_EQUAL, self.assertTrue(Gst.value_compare(refval, value) == Gst.VALUE_EQUAL,
"%s are not equal: %s != %s" % (pname, v0, v1)) "%s are not equal: %s != %s\n %s != %s" % (pname, value, refval, element, ref))
if isinstance(ref, GES.TrackElement): if isinstance(ref, GES.TrackElement):
self.assertElementAreEqual(ref.get_nleobject(), element.get_nleobject()) self.assertElementAreEqual(ref.get_nleobject(), element.get_nleobject())
@ -275,7 +275,7 @@ class GESSimpleTimelineTest(GESTest):
if not isinstance(ref_child, GES.Effect): if not isinstance(ref_child, GES.Effect):
child = tmpchild child = tmpchild
break break
elif ref_child.props.bin_description == child.props.bin_description: elif ref_child.props.bin_description == tmpchild.props.bin_description:
child = tmpchild child = tmpchild
break break

View file

@ -613,9 +613,8 @@ class TestEditing(common.GESSimpleTimelineTest):
def test_trim_time_effects(self): def test_trim_time_effects(self):
self.track_types = [GES.TrackType.VIDEO] self.track_types = [GES.TrackType.VIDEO]
super().setUp() super().setUp()
clip = self.append_clip() clip = self.append_clip(asset_id="max-duration=30")
self.assertTrue(clip.set_inpoint(12)) self.assertTrue(clip.set_inpoint(12))
self.assertTrue(clip.set_max_duration(30))
self.assertEqual(clip.get_duration_limit(), 18) self.assertEqual(clip.get_duration_limit(), 18)
children = clip.get_children(False) children = clip.get_children(False)
@ -646,6 +645,7 @@ class TestEditing(common.GESSimpleTimelineTest):
self.assertEqual(clip.get_duration_limit(), 36) self.assertEqual(clip.get_duration_limit(), 36)
self.assertTrue(clip.set_start(40)) self.assertTrue(clip.set_start(40))
self.assertTrue(clip.set_duration(10)) self.assertTrue(clip.set_duration(10))
self.check_reload_timeline()
# cannot trim to a 16 because overlay would have a negative in-point # cannot trim to a 16 because overlay would have a negative in-point
error = None error = None
@ -662,6 +662,8 @@ class TestEditing(common.GESSimpleTimelineTest):
self.assertEqual(overlay.get_inpoint(), 5) self.assertEqual(overlay.get_inpoint(), 5)
self.assertEqual(overlay.get_max_duration(), 16) self.assertEqual(overlay.get_max_duration(), 16)
self.check_reload_timeline()
# trim backwards to 20 # trim backwards to 20
self.assertTrue( self.assertTrue(
clip.edit_full(-1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 20)) clip.edit_full(-1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 20))
@ -686,6 +688,7 @@ class TestEditing(common.GESSimpleTimelineTest):
# increased by 2 # increased by 2
self.assertEqual(overlay.get_inpoint(), 2) self.assertEqual(overlay.get_inpoint(), 2)
self.assertEqual(overlay.get_max_duration(), 16) self.assertEqual(overlay.get_max_duration(), 16)
self.check_reload_timeline()
def test_ripple_end(self): def test_ripple_end(self):
clip = self.append_clip() clip = self.append_clip()