From dc4ca15ba803ea65ec705be4eee8730e9280f31c Mon Sep 17 00:00:00 2001 From: Henry Wilkes Date: Tue, 3 Mar 2020 18:00:51 +0000 Subject: [PATCH] clip: copy and paste control bindings Previously the control bindings were not properly copied into the pasted clip. Also changed the order so that elements are added to the clip before the clip is added to the timeline. --- ges/ges-clip.c | 24 ++--- tests/check/ges/clip.c | 196 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 210 insertions(+), 10 deletions(-) diff --git a/ges/ges-clip.c b/ges/ges-clip.c index d57f3f5b11..533ab22e7a 100644 --- a/ges/ges-clip.c +++ b/ges/ges-clip.c @@ -727,7 +727,9 @@ _deep_copy (GESTimelineElement * element, GESTimelineElement * copy) for (tmp = GES_CONTAINER_CHILDREN (element); tmp; tmp = tmp->next) { el = GES_TRACK_ELEMENT (tmp->data); + /* copies the children properties */ el_copy = ges_track_element_copy_core (el, TRUE); + ges_track_element_copy_bindings (el, el_copy, GST_CLOCK_TIME_NONE); ccopy->priv->copied_track_elements = g_list_append (ccopy->priv->copied_track_elements, el_copy); } @@ -745,17 +747,7 @@ _paste (GESTimelineElement * element, GESTimelineElement * ref, if (self->priv->copied_layer) nclip->priv->copied_layer = g_object_ref (self->priv->copied_layer); - ges_timeline_element_set_start (GES_TIMELINE_ELEMENT (nclip), paste_position); - if (self->priv->copied_layer) { - if (!ges_layer_add_clip (self->priv->copied_layer, nclip)) { - GST_INFO ("%" GES_FORMAT " could not be pasted to %" GST_TIME_FORMAT, - GES_ARGS (element), GST_TIME_ARGS (paste_position)); - - return NULL; - } - - } for (tmp = self->priv->copied_track_elements; tmp; tmp = tmp->next) { GESTrackElement *new_trackelement, *trackelement = @@ -781,6 +773,18 @@ _paste (GESTimelineElement * element, GESTimelineElement * ref, GST_CLOCK_TIME_NONE); } + /* FIXME: should we bypass the select-tracks-for-object signal when + * copying and pasting? */ + if (self->priv->copied_layer) { + if (!ges_layer_add_clip (self->priv->copied_layer, nclip)) { + GST_INFO ("%" GES_FORMAT " could not be pasted to %" GST_TIME_FORMAT, + GES_ARGS (element), GST_TIME_ARGS (paste_position)); + + return NULL; + } + + } + return GES_TIMELINE_ELEMENT (nclip); } diff --git a/tests/check/ges/clip.c b/tests/check/ges/clip.c index 68b146268f..3a4f1ee856 100644 --- a/tests/check/ges/clip.c +++ b/tests/check/ges/clip.c @@ -1230,6 +1230,201 @@ GST_START_TEST (test_children_properties_change) GST_END_TEST; +static GESTimelineElement * +_el_with_child_prop (GESTimelineElement * clip, GObject * prop_child, + GParamSpec * prop) +{ + GList *tmp; + GESTimelineElement *child = NULL; + for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next) { + GObject *found_child; + GParamSpec *found_prop; + if (ges_timeline_element_lookup_child (tmp->data, prop->name, + &found_child, &found_prop)) { + if (found_child == prop_child && found_prop == prop) { + child = tmp->data; + /* break early, but still free */ + tmp->next = NULL; + } + g_param_spec_unref (found_prop); + g_object_unref (found_child); + } + } + return child; +} + +static GstTimedValue * +_new_timed_value (GstClockTime time, gdouble val) +{ + GstTimedValue *tmval = g_new0 (GstTimedValue, 1); + tmval->value = val; + tmval->timestamp = time; + return tmval; +} + +#define _assert_binding(element, prop_name, child, timed_vals, mode) \ +{ \ + GstInterpolationMode found_mode; \ + GSList *tmp1; \ + GList *tmp2; \ + guint i; \ + GList *found_timed_vals; \ + GObject *found_object = NULL; \ + GstControlSource *source = NULL; \ + GstControlBinding *binding = ges_track_element_get_control_binding ( \ + GES_TRACK_ELEMENT (element), prop_name); \ + fail_unless (binding, "No control binding found for %s on %s", \ + prop_name, element->name); \ + g_object_get (G_OBJECT (binding), "control-source", &source, \ + "object", &found_object, NULL); \ + \ + fail_unless (found_object == child); \ + g_object_unref (found_object); \ + \ + fail_unless (GST_IS_INTERPOLATION_CONTROL_SOURCE (source)); \ + found_timed_vals = gst_timed_value_control_source_get_all ( \ + GST_TIMED_VALUE_CONTROL_SOURCE (source)); \ + \ + for (i = 0, tmp1 = timed_vals, tmp2 = found_timed_vals; tmp1 && tmp2; \ + tmp1 = tmp1->next, tmp2 = tmp2->next, i++) { \ + GstTimedValue *val1 = tmp1->data, *val2 = tmp2->data; \ + fail_unless (val1->timestamp == val2->timestamp && \ + val1->value == val2->value, "The %ith timed value (%lu: %g) " \ + "does not match the found timed value (%lu: %g)", \ + i, val1->timestamp, val1->value, val2->timestamp, val2->value); \ + } \ + fail_unless (tmp1 == NULL, "Found too few timed values"); \ + fail_unless (tmp2 == NULL, "Found too many timed values"); \ + \ + g_list_free (found_timed_vals); \ + g_object_get (G_OBJECT (source), "mode", &found_mode, NULL); \ + fail_unless (found_mode == GST_INTERPOLATION_MODE_CUBIC); \ + g_object_unref (source); \ +} + +GST_START_TEST (test_copy_paste_children_properties) +{ + GESTimeline *timeline; + GESLayer *layer; + GESTimelineElement *clip, *copy, *pasted, *track_el, *pasted_el; + GObject *sub_child, *pasted_sub_child; + GParamSpec **orig_props, **pasted_props, **track_el_props, **pasted_el_props; + guint num_orig_props, num_pasted_props, num_track_el_props, + num_pasted_el_props; + GParamSpec *prop, *found_prop; + GValue val = G_VALUE_INIT; + GSList *timed_vals; + GstControlSource *source; + + ges_init (); + + timeline = ges_timeline_new_audio_video (); + layer = ges_timeline_append_layer (timeline); + clip = GES_TIMELINE_ELEMENT (ges_test_clip_new ()); + ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (clip), 50); + + fail_unless (ges_layer_add_clip (layer, GES_CLIP (clip))); + + /* get children properties */ + orig_props = + ges_timeline_element_list_children_properties (clip, &num_orig_props); + fail_unless (num_orig_props); + + /* focus on one property */ + fail_unless (ges_timeline_element_lookup_child (clip, "posx", + &sub_child, &prop)); + g_value_init (&val, G_TYPE_INT); + g_value_set_int (&val, 30); + fail_unless (ges_timeline_element_set_child_property (clip, "posx", &val)); + g_value_unset (&val); + + _assert_int_val_child_prop (clip, val, 30, prop, "posx"); + + /* find the track element where the child property comes from */ + fail_unless (track_el = _el_with_child_prop (clip, sub_child, prop)); + _assert_int_val_child_prop (track_el, val, 30, prop, "posx"); + + track_el_props = + ges_timeline_element_list_children_properties (track_el, + &num_track_el_props); + + /* set a control binding */ + timed_vals = g_slist_prepend (NULL, _new_timed_value (200, 5)); + timed_vals = g_slist_prepend (timed_vals, _new_timed_value (40, 50)); + timed_vals = g_slist_prepend (timed_vals, _new_timed_value (20, 10)); + timed_vals = g_slist_prepend (timed_vals, _new_timed_value (0, 20)); + + source = GST_CONTROL_SOURCE (gst_interpolation_control_source_new ()); + g_object_set (G_OBJECT (source), "mode", GST_INTERPOLATION_MODE_CUBIC, NULL); + fail_unless (gst_timed_value_control_source_set_from_list + (GST_TIMED_VALUE_CONTROL_SOURCE (source), timed_vals)); + + fail_unless (ges_track_element_set_control_source (GES_TRACK_ELEMENT + (track_el), source, "posx", "direct-absolute")); + g_object_unref (source); + + /* check the control binding */ + _assert_binding (track_el, "posx", sub_child, timed_vals, + GST_INTERPOLATION_MODE_CUBIC); + + /* copy and paste */ + fail_unless (copy = ges_timeline_element_copy (clip, TRUE)); + fail_unless (pasted = ges_timeline_element_paste (copy, 30)); + + gst_object_unref (copy); + + /* test that the new clip has the same child properties */ + pasted_props = + ges_timeline_element_list_children_properties (pasted, &num_pasted_props); + + assert_property_list_match (pasted_props, num_pasted_props, + orig_props, num_orig_props); + + /* get the details for the copied 'prop' property */ + fail_unless (ges_timeline_element_lookup_child (pasted, + "posx", &pasted_sub_child, &found_prop)); + fail_unless (found_prop == prop); + g_param_spec_unref (found_prop); + fail_unless (G_OBJECT_TYPE (pasted_sub_child) == G_OBJECT_TYPE (sub_child)); + + _assert_int_val_child_prop (pasted, val, 30, prop, "posx"); + + /* get the associated child */ + fail_unless (pasted_el = + _el_with_child_prop (pasted, pasted_sub_child, prop)); + _assert_int_val_child_prop (pasted_el, val, 30, prop, "posx"); + + pasted_el_props = + ges_timeline_element_list_children_properties (pasted_el, + &num_pasted_el_props); + + assert_property_list_match (pasted_el_props, num_pasted_el_props, + track_el_props, num_track_el_props); + + /* check the control binding on the pasted element */ + _assert_binding (pasted_el, "posx", pasted_sub_child, timed_vals, + GST_INTERPOLATION_MODE_CUBIC); + + + /* free */ + g_slist_free_full (timed_vals, g_free); + + free_children_properties (pasted_props, num_pasted_props); + free_children_properties (orig_props, num_orig_props); + free_children_properties (pasted_el_props, num_pasted_el_props); + free_children_properties (track_el_props, num_track_el_props); + + g_param_spec_unref (prop); + g_object_unref (pasted_sub_child); + g_object_unref (sub_child); + gst_object_unref (timeline); + + ges_deinit (); +} + +GST_END_TEST; + + static Suite * ges_suite (void) @@ -1251,6 +1446,7 @@ ges_suite (void) tcase_add_test (tc_chain, test_can_add_effect); tcase_add_test (tc_chain, test_children_properties_contain); tcase_add_test (tc_chain, test_children_properties_change); + tcase_add_test (tc_chain, test_copy_paste_children_properties); return s; } -- 2.34.1