element: Implement a paste method

Allowing user to copy paste clips very easily
This commit is contained in:
Thibault Saunier 2015-06-29 18:04:32 +02:00
parent d23e43ae1a
commit 5885f58c14
8 changed files with 222 additions and 43 deletions

View file

@ -454,6 +454,7 @@ ges_timeline_element_roll_end
ges_timeline_element_trim
ges_timeline_element_get_toplevel_parent
ges_timeline_element_copy
ges_timeline_element_paste
ges_timeline_element_get_name
ges_timeline_element_set_name
ges_timeline_element_list_children_properties

View file

@ -625,6 +625,44 @@ _edit (GESContainer * container, GList * layers,
return ret;
}
static gboolean
_paste (GESTimelineElement * element, GESTimelineElement * ref,
GstClockTime paste_position)
{
GList *tmp;
GESClip *self = GES_CLIP (element);
GESClip *refclip = GES_CLIP (ref);
ges_clip_set_moving_from_layer (self, TRUE);
ges_layer_add_clip (refclip->priv->layer, self);
ges_clip_set_moving_from_layer (self, FALSE);
ges_timeline_element_set_start (GES_TIMELINE_ELEMENT (self), paste_position);
for (tmp = GES_CONTAINER_CHILDREN (refclip); tmp; tmp = tmp->next) {
GESTrackElement *new_trackelement, *trackelement =
GES_TRACK_ELEMENT (tmp->data);
new_trackelement =
GES_TRACK_ELEMENT (ges_timeline_element_copy (GES_TIMELINE_ELEMENT
(trackelement), FALSE));
if (new_trackelement == NULL) {
GST_WARNING_OBJECT (trackelement, "Could not create a copy");
continue;
}
ges_container_add (GES_CONTAINER (self),
GES_TIMELINE_ELEMENT (new_trackelement));
ges_track_element_copy_properties (GES_TIMELINE_ELEMENT (trackelement),
GES_TIMELINE_ELEMENT (new_trackelement));
ges_track_element_copy_bindings (trackelement, new_trackelement,
GST_CLOCK_TIME_NONE);
}
return TRUE;
}
/****************************************************
@ -715,7 +753,7 @@ ges_clip_class_init (GESClipClass * klass)
element_class->set_inpoint = _set_inpoint;
element_class->set_priority = _set_priority;
element_class->set_max_duration = _set_max_duration;
/* TODO implement the deep_copy Virtual method */
element_class->paste = _paste;
container_class->add_child = _add_child;
container_class->remove_child = _remove_child;
@ -1245,7 +1283,7 @@ ges_clip_split (GESClip * clip, guint64 position)
ges_track_element_copy_properties (GES_TIMELINE_ELEMENT (trackelement),
GES_TIMELINE_ELEMENT (new_trackelement));
ges_track_element_split_bindings (trackelement, new_trackelement,
ges_track_element_copy_bindings (trackelement, new_trackelement,
position - start + inpoint);
}

View file

@ -283,6 +283,31 @@ _get_track_types (GESTimelineElement * object)
return types ^ GES_TRACK_TYPE_UNKNOWN;
}
static gboolean
_paste (GESTimelineElement * element, GESTimelineElement * ref,
GstClockTime paste_position)
{
GList *tmp;
GESContainer *self = GES_CONTAINER (element);
GESContainer *refcontainer = GES_CONTAINER (ref);
for (tmp = GES_CONTAINER_CHILDREN (refcontainer); tmp; tmp = tmp->next) {
ChildMapping *map;
GESTimelineElement *child, *refchild = GES_TIMELINE_ELEMENT (tmp->data);
map = g_hash_table_lookup (refcontainer->priv->mappings, refchild);
child = ges_timeline_element_copy (GES_TIMELINE_ELEMENT (refchild), TRUE);
ges_timeline_element_paste (child, paste_position + map->start_offset);
ges_timeline_element_set_timeline (element,
GES_TIMELINE_ELEMENT_TIMELINE (ref));
ges_container_add (self, child);
}
return TRUE;
}
/******************************************
* *
* GObject virtual methods implementation *
@ -382,6 +407,7 @@ ges_container_class_init (GESContainerClass * klass)
element_class->list_children_properties = _list_children_properties;
element_class->lookup_child = _lookup_child;
element_class->get_track_types = _get_track_types;
element_class->paste = _paste;
/* No default implementations */
klass->remove_child = NULL;

View file

@ -598,6 +598,23 @@ _group (GList * containers)
return ret;
}
static gboolean
_paste (GESTimelineElement * element, GESTimelineElement * ref,
GstClockTime paste_position)
{
if (GES_TIMELINE_ELEMENT_CLASS (parent_class)->paste (element,
ref, paste_position)) {
if (GES_CONTAINER_CHILDREN (element))
timeline_add_group (GES_TIMELINE_ELEMENT_TIMELINE (GES_CONTAINER_CHILDREN
(element)->data), GES_GROUP (element));
return TRUE;
}
return FALSE;
}
/****************************************************
* *
@ -675,6 +692,7 @@ ges_group_class_init (GESGroupClass * klass)
element_class->set_inpoint = _set_inpoint;
element_class->set_start = _set_start;
element_class->set_priority = _set_priority;
element_class->paste = _paste;
/* We override start, inpoint, duration and max-duration from GESTimelineElement
* in order to makes sure those fields are not serialized.

View file

@ -339,7 +339,7 @@ G_GNUC_INTERNAL guint32 _ges_track_element_get_layer_priority (GESTrackElement
G_GNUC_INTERNAL void ges_track_element_copy_properties (GESTimelineElement * element,
GESTimelineElement * elementcopy);
G_GNUC_INTERNAL void ges_track_element_split_bindings (GESTrackElement *element,
G_GNUC_INTERNAL void ges_track_element_copy_bindings (GESTrackElement *element,
GESTrackElement *new_element,
guint64 position);

View file

@ -94,6 +94,8 @@ struct _GESTimelineElementPrivate
* The hashtable should look like
* {GParamaSpec ---> child}*/
GHashTable *children_props;
GESTimelineElement *copied_from;
};
static gboolean
@ -240,6 +242,14 @@ _set_property (GObject * object, guint property_id,
}
}
static void
ges_timeline_element_dispose (GObject * object)
{
GESTimelineElement *self = GES_TIMELINE_ELEMENT (object);
g_clear_object (&self->priv->copied_from);
}
static void
ges_timeline_element_finalize (GObject * self)
{
@ -387,6 +397,7 @@ ges_timeline_element_class_init (GESTimelineElementClass * klass)
G_SIGNAL_NO_HOOKS, 0, NULL, NULL, g_cclosure_marshal_generic,
G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_PARAM);
object_class->dispose = ges_timeline_element_dispose;
object_class->finalize = ges_timeline_element_finalize;
klass->set_parent = NULL;
@ -1113,6 +1124,10 @@ ges_timeline_element_copy (GESTimelineElement * self, gboolean deep)
" on class %s. Can not finish the copy", G_OBJECT_CLASS_NAME (klass));
}
if (deep) {
ret->priv->copied_from = gst_object_ref (self);
}
return ret;
}
@ -1661,8 +1676,48 @@ ges_timeline_element_remove_child_property (GESTimelineElement * self,
GESTrackType
ges_timeline_element_get_track_types (GESTimelineElement * self)
{
g_return_if_fail (GES_IS_TIMELINE_ELEMENT (self));
g_return_if_fail (GES_TIMELINE_ELEMENT_GET_CLASS (self)->get_track_types);
g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), 0);
g_return_val_if_fail (GES_TIMELINE_ELEMENT_GET_CLASS (self)->get_track_types,
0);
return GES_TIMELINE_ELEMENT_GET_CLASS (self)->get_track_types (self);
}
/**
* ges_timeline_element_paste:
* @self: The #GESTimelineElement to paste
* @paste_position: The position in the timeline the element should
* be copied to, meaning it will become the start of @self
*
* Paste @self inside the timeline. @self must have been created
* using ges_timeline_element_copy with recurse=TRUE set,
* otherwise it will fail.
*
* Since: 1.6.0
*/
gboolean
ges_timeline_element_paste (GESTimelineElement * self,
GstClockTime paste_position)
{
gboolean res;
g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
if (!self->priv->copied_from) {
GST_ERROR_OBJECT (self, "Is not being 'deeply' copied!");
return FALSE;
}
if (!GES_TIMELINE_ELEMENT_GET_CLASS (self)->paste) {
GST_ERROR_OBJECT (self, "No paste vmethod implemented");
return FALSE;
}
res = GES_TIMELINE_ELEMENT_GET_CLASS (self)->paste (self,
self->priv->copied_from, paste_position);
g_clear_object (&self->priv->copied_from);
return res;
}

View file

@ -186,6 +186,8 @@ struct _GESTimelineElementClass
gboolean (*roll_end) (GESTimelineElement *self, guint64 end);
gboolean (*trim) (GESTimelineElement *self, guint64 start);
void (*deep_copy) (GESTimelineElement *self, GESTimelineElement *copy);
gboolean (*paste) (GESTimelineElement *self, GESTimelineElement *ref_element,
GstClockTime paste_position);
GParamSpec** (*list_children_properties) (GESTimelineElement * self, guint *n_properties);
gboolean (*lookup_child) (GESTimelineElement *self, const gchar *prop_name,
@ -277,6 +279,9 @@ gboolean ges_timeline_element_add_child_property (GESTimelineElement * self,
gboolean ges_timeline_element_remove_child_property(GESTimelineElement * self,
GParamSpec *pspec);
gboolean ges_timeline_element_paste (GESTimelineElement * self,
GstClockTime paste_position);
GESTrackType ges_timeline_element_get_track_types (GESTimelineElement * self);
G_END_DECLS

View file

@ -1282,12 +1282,77 @@ ges_track_element_copy_properties (GESTimelineElement * element,
g_free (specs);
}
static void
_split_binding (GESTrackElement * element, GESTrackElement * new_element,
guint64 position, GstTimedValueControlSource * source,
GstTimedValueControlSource * new_source, gboolean absolute)
{
GstTimedValue *last_value = NULL;
gboolean past_position = FALSE;
GList *values, *tmp;
values =
gst_timed_value_control_source_get_all (GST_TIMED_VALUE_CONTROL_SOURCE
(source));
for (tmp = values; tmp; tmp = tmp->next) {
GstTimedValue *value = tmp->data;
if (value->timestamp > position) {
gfloat value_at_pos;
/* FIXME We should be able to use gst_control_source_get_value so
* all modes are handled. Right now that method only works if the value
* we are looking for is between two actual keyframes which is not enough
* in our case. bug #706621 */
value_at_pos =
interpolate_values_for_position (last_value, value, position,
absolute);
past_position = TRUE;
gst_timed_value_control_source_set (new_source, position, value_at_pos);
gst_timed_value_control_source_set (new_source, value->timestamp,
value->value);
gst_timed_value_control_source_unset (source, value->timestamp);
gst_timed_value_control_source_set (source, position, value_at_pos);
} else if (past_position) {
gst_timed_value_control_source_unset (source, value->timestamp);
gst_timed_value_control_source_set (new_source, value->timestamp,
value->value);
}
last_value = value;
}
}
static void
_copy_binding (GESTrackElement * element, GESTrackElement * new_element,
guint64 position, GstTimedValueControlSource * source,
GstTimedValueControlSource * new_source, gboolean absolute)
{
GList *values, *tmp;
values =
gst_timed_value_control_source_get_all (GST_TIMED_VALUE_CONTROL_SOURCE
(source));
for (tmp = values; tmp; tmp = tmp->next) {
GstTimedValue *value = tmp->data;
gst_timed_value_control_source_set (new_source, value->timestamp,
value->value);
}
}
/* position == GST_CLOCK_TIME_NONE means that we do a simple copy
* other position means that the function will do a splitting
* and thus interpollate the values in the element and new_element
*/
void
ges_track_element_split_bindings (GESTrackElement * element,
ges_track_element_copy_bindings (GESTrackElement * element,
GESTrackElement * new_element, guint64 position)
{
GParamSpec **specs;
guint n, n_specs;
gboolean absolute;
GstControlBinding *binding;
GstTimedValueControlSource *source, *new_source;
@ -1295,60 +1360,31 @@ ges_track_element_split_bindings (GESTrackElement * element,
ges_track_element_list_children_properties (GES_TRACK_ELEMENT (element),
&n_specs);
for (n = 0; n < n_specs; ++n) {
GList *values, *tmp;
GstTimedValue *last_value = NULL;
gboolean past_position = FALSE, absolute;
GstInterpolationMode mode;
binding = ges_track_element_get_control_binding (element, specs[n]->name);
if (!binding)
continue;
g_object_get (binding, "control_source", &source, NULL);
/* FIXME : this should work as well with other types of control sources */
g_object_get (binding, "control_source", &source, NULL);
if (!GST_IS_TIMED_VALUE_CONTROL_SOURCE (source))
continue;
g_object_get (binding, "absolute", &absolute, NULL);
g_object_get (source, "mode", &mode, NULL);
new_source =
GST_TIMED_VALUE_CONTROL_SOURCE (gst_interpolation_control_source_new
());
g_object_get (source, "mode", &mode, NULL);
g_object_set (new_source, "mode", mode, NULL);
values =
gst_timed_value_control_source_get_all (GST_TIMED_VALUE_CONTROL_SOURCE
(source));
for (tmp = values; tmp; tmp = tmp->next) {
GstTimedValue *value = tmp->data;
if (value->timestamp > position) {
gfloat value_at_pos;
/* FIXME We should be able to use gst_control_source_get_value so
* all modes are handled. Right now that method only works if the value
* we are looking for is between two actual keyframes which is not enough
* in our case. bug #706621 */
value_at_pos =
interpolate_values_for_position (last_value, value, position,
absolute);
past_position = TRUE;
gst_timed_value_control_source_set (new_source, position, value_at_pos);
gst_timed_value_control_source_set (new_source, value->timestamp,
value->value);
gst_timed_value_control_source_unset (source, value->timestamp);
gst_timed_value_control_source_set (source, position, value_at_pos);
} else if (past_position) {
gst_timed_value_control_source_unset (source, value->timestamp);
gst_timed_value_control_source_set (new_source, value->timestamp,
value->value);
}
last_value = value;
}
if (GST_CLOCK_TIME_IS_VALID (position))
_split_binding (element, new_element, position, source, new_source,
absolute);
else
_copy_binding (element, new_element, position, source, new_source,
absolute);
/* We only manage direct (absolute) bindings, see TODO in set_control_source */
if (absolute)