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.
This commit is contained in:
Henry Wilkes 2020-03-03 18:00:51 +00:00
parent c63fb5db0e
commit dc4ca15ba8
2 changed files with 210 additions and 10 deletions

View file

@ -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);
}

View file

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