mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 19:51:11 +00:00
element: Implement a paste method
Allowing user to copy paste clips very easily
This commit is contained in:
parent
d23e43ae1a
commit
5885f58c14
8 changed files with 222 additions and 43 deletions
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue