mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-17 03:35:21 +00:00
ges: Make setting start/duration move or trim generic
We were implementing the logic for moving/trimming elements specific to SourceClip but this was not correct ass the new timeline tree allows us to handle that for all element types in a generic and nice way. This make us need to have groups trimming properly implemented in the timeline tree, leading to some fixes in the group tests. This adds tests for the various cases known to not be handled properly by the previous code. Fixes https://gitlab.freedesktop.org/gstreamer/gst-editing-services/issues/92
This commit is contained in:
parent
b66290d1be
commit
6b7c658b6a
9 changed files with 338 additions and 92 deletions
|
@ -91,9 +91,11 @@ neighbour_changed_cb (GESClip * clip, GParamSpec * arg G_GNUC_UNUSED,
|
||||||
}
|
}
|
||||||
|
|
||||||
self->positioning = TRUE;
|
self->positioning = TRUE;
|
||||||
|
ELEMENT_SET_FLAG (self->transition_clip, GES_TIMELINE_ELEMENT_SET_SIMPLE);
|
||||||
_set_start0 (GES_TIMELINE_ELEMENT (self->transition_clip),
|
_set_start0 (GES_TIMELINE_ELEMENT (self->transition_clip),
|
||||||
_START (self->next_source));
|
_START (self->next_source));
|
||||||
_set_duration0 (GES_TIMELINE_ELEMENT (self->transition_clip), new_duration);
|
_set_duration0 (GES_TIMELINE_ELEMENT (self->transition_clip), new_duration);
|
||||||
|
ELEMENT_SET_FLAG (self->transition_clip, GES_TIMELINE_ELEMENT_SET_SIMPLE);
|
||||||
self->positioning = FALSE;
|
self->positioning = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -218,8 +218,11 @@ _set_duration (GESTimelineElement * element, GstClockTime duration)
|
||||||
for (tmp = container->children; tmp; tmp = g_list_next (tmp)) {
|
for (tmp = container->children; tmp; tmp = g_list_next (tmp)) {
|
||||||
GESTimelineElement *child = (GESTimelineElement *) tmp->data;
|
GESTimelineElement *child = (GESTimelineElement *) tmp->data;
|
||||||
|
|
||||||
if (child != container->initiated_move)
|
if (child != container->initiated_move) {
|
||||||
|
ELEMENT_SET_FLAG (child, GES_TIMELINE_ELEMENT_SET_SIMPLE);
|
||||||
_set_duration0 (GES_TIMELINE_ELEMENT (child), duration);
|
_set_duration0 (GES_TIMELINE_ELEMENT (child), duration);
|
||||||
|
ELEMENT_UNSET_FLAG (child, GES_TIMELINE_ELEMENT_SET_SIMPLE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
container->children_control_mode = GES_CHILDREN_UPDATE;
|
container->children_control_mode = GES_CHILDREN_UPDATE;
|
||||||
|
|
||||||
|
|
|
@ -46,49 +46,6 @@ enum
|
||||||
|
|
||||||
G_DEFINE_TYPE_WITH_PRIVATE (GESSourceClip, ges_source_clip, GES_TYPE_CLIP);
|
G_DEFINE_TYPE_WITH_PRIVATE (GESSourceClip, ges_source_clip, GES_TYPE_CLIP);
|
||||||
|
|
||||||
static gboolean
|
|
||||||
_set_start (GESTimelineElement * element, GstClockTime start)
|
|
||||||
{
|
|
||||||
GESTimelineElement *toplevel =
|
|
||||||
ges_timeline_element_get_toplevel_parent (element);
|
|
||||||
|
|
||||||
gst_object_unref (toplevel);
|
|
||||||
if (element->timeline
|
|
||||||
&& !ELEMENT_FLAG_IS_SET (element, GES_TIMELINE_ELEMENT_SET_SIMPLE)
|
|
||||||
&& !ELEMENT_FLAG_IS_SET (toplevel, GES_TIMELINE_ELEMENT_SET_SIMPLE)) {
|
|
||||||
if (!ges_timeline_move_object_simple (element->timeline, element, NULL,
|
|
||||||
GES_EDGE_NONE, start))
|
|
||||||
return FALSE;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
GES_TIMELINE_ELEMENT_CLASS (ges_source_clip_parent_class)->set_start
|
|
||||||
(element, start);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
_set_duration (GESTimelineElement * element, GstClockTime duration)
|
|
||||||
{
|
|
||||||
GESTimelineElement *toplevel =
|
|
||||||
ges_timeline_element_get_toplevel_parent (element);
|
|
||||||
|
|
||||||
gst_object_unref (toplevel);
|
|
||||||
if (element->timeline
|
|
||||||
&& !ELEMENT_FLAG_IS_SET (element, GES_TIMELINE_ELEMENT_SET_SIMPLE)
|
|
||||||
&& !ELEMENT_FLAG_IS_SET (toplevel, GES_TIMELINE_ELEMENT_SET_SIMPLE)) {
|
|
||||||
if (!timeline_trim_object (element->timeline, element,
|
|
||||||
GES_TIMELINE_ELEMENT_LAYER_PRIORITY (element), NULL, GES_EDGE_END,
|
|
||||||
element->start + duration))
|
|
||||||
return FALSE;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
GES_TIMELINE_ELEMENT_CLASS (ges_source_clip_parent_class)->set_duration
|
|
||||||
(element, duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ges_source_clip_get_property (GObject * object, guint property_id,
|
ges_source_clip_get_property (GObject * object, guint property_id,
|
||||||
GValue * value, GParamSpec * pspec)
|
GValue * value, GParamSpec * pspec)
|
||||||
|
@ -119,14 +76,10 @@ static void
|
||||||
ges_source_clip_class_init (GESSourceClipClass * klass)
|
ges_source_clip_class_init (GESSourceClipClass * klass)
|
||||||
{
|
{
|
||||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
GESTimelineElementClass *element_class = GES_TIMELINE_ELEMENT_CLASS (klass);
|
|
||||||
|
|
||||||
object_class->get_property = ges_source_clip_get_property;
|
object_class->get_property = ges_source_clip_get_property;
|
||||||
object_class->set_property = ges_source_clip_set_property;
|
object_class->set_property = ges_source_clip_set_property;
|
||||||
object_class->finalize = ges_source_clip_finalize;
|
object_class->finalize = ges_source_clip_finalize;
|
||||||
|
|
||||||
element_class->set_start = _set_start;
|
|
||||||
element_class->set_duration = _set_duration;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
|
@ -972,6 +972,7 @@ ges_timeline_element_get_timeline (GESTimelineElement * self)
|
||||||
gboolean
|
gboolean
|
||||||
ges_timeline_element_set_start (GESTimelineElement * self, GstClockTime start)
|
ges_timeline_element_set_start (GESTimelineElement * self, GstClockTime start)
|
||||||
{
|
{
|
||||||
|
gboolean emit_notify = TRUE;
|
||||||
GESTimelineElementClass *klass;
|
GESTimelineElementClass *klass;
|
||||||
GESTimelineElement *toplevel_container, *parent;
|
GESTimelineElement *toplevel_container, *parent;
|
||||||
|
|
||||||
|
@ -980,13 +981,24 @@ ges_timeline_element_set_start (GESTimelineElement * self, GstClockTime start)
|
||||||
if (self->start == start)
|
if (self->start == start)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (self, "current start: %" GST_TIME_FORMAT
|
GST_DEBUG_OBJECT (self, "current start: %" GST_TIME_FORMAT
|
||||||
" new start: %" GST_TIME_FORMAT,
|
" new start: %" GST_TIME_FORMAT,
|
||||||
GST_TIME_ARGS (GES_TIMELINE_ELEMENT_START (self)), GST_TIME_ARGS (start));
|
GST_TIME_ARGS (GES_TIMELINE_ELEMENT_START (self)), GST_TIME_ARGS (start));
|
||||||
|
|
||||||
toplevel_container = ges_timeline_element_get_toplevel_parent (self);
|
toplevel_container = ges_timeline_element_get_toplevel_parent (self);
|
||||||
|
|
||||||
|
if (self->timeline
|
||||||
|
&& !ELEMENT_FLAG_IS_SET (self, GES_TIMELINE_ELEMENT_SET_SIMPLE)
|
||||||
|
&& !ELEMENT_FLAG_IS_SET (toplevel_container,
|
||||||
|
GES_TIMELINE_ELEMENT_SET_SIMPLE)) {
|
||||||
|
if (!ges_timeline_move_object_simple (self->timeline, self, NULL,
|
||||||
|
GES_EDGE_NONE, start)) {
|
||||||
|
gst_object_unref (toplevel_container);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit_notify = FALSE;
|
||||||
|
}
|
||||||
parent = self->parent;
|
parent = self->parent;
|
||||||
|
|
||||||
/* FIXME This should not belong to GESTimelineElement */
|
/* FIXME This should not belong to GESTimelineElement */
|
||||||
|
@ -1003,9 +1015,10 @@ ges_timeline_element_set_start (GESTimelineElement * self, GstClockTime start)
|
||||||
}
|
}
|
||||||
|
|
||||||
gst_object_unref (toplevel_container);
|
gst_object_unref (toplevel_container);
|
||||||
|
klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
|
||||||
if (klass->set_start) {
|
if (klass->set_start) {
|
||||||
gint res = klass->set_start (self, start);
|
gint res = klass->set_start (self, start);
|
||||||
if (res == TRUE) {
|
if (res == TRUE && emit_notify) {
|
||||||
self->start = start;
|
self->start = start;
|
||||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_START]);
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_START]);
|
||||||
}
|
}
|
||||||
|
@ -1126,19 +1139,40 @@ ges_timeline_element_set_duration (GESTimelineElement * self,
|
||||||
GstClockTime duration)
|
GstClockTime duration)
|
||||||
{
|
{
|
||||||
GESTimelineElementClass *klass;
|
GESTimelineElementClass *klass;
|
||||||
|
gboolean emit_notify = TRUE;
|
||||||
|
GESTimelineElement *toplevel;
|
||||||
|
|
||||||
g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
|
g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
|
||||||
|
|
||||||
klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
|
toplevel = ges_timeline_element_get_toplevel_parent (self);
|
||||||
|
if (self->timeline &&
|
||||||
|
!ELEMENT_FLAG_IS_SET (self, GES_TIMELINE_ELEMENT_SET_SIMPLE) &&
|
||||||
|
!ELEMENT_FLAG_IS_SET (toplevel, GES_TIMELINE_ELEMENT_SET_SIMPLE)) {
|
||||||
|
gboolean res;
|
||||||
|
|
||||||
|
res = timeline_trim_object (self->timeline, self,
|
||||||
|
GES_TIMELINE_ELEMENT_LAYER_PRIORITY (self), NULL,
|
||||||
|
GES_EDGE_END, self->start + duration);
|
||||||
|
|
||||||
|
if (!res) {
|
||||||
|
gst_object_unref (toplevel);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit_notify = res == -1;
|
||||||
|
}
|
||||||
|
gst_object_unref (toplevel);
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (self, "current duration: %" GST_TIME_FORMAT
|
GST_DEBUG_OBJECT (self, "current duration: %" GST_TIME_FORMAT
|
||||||
" new duration: %" GST_TIME_FORMAT,
|
" new duration: %" GST_TIME_FORMAT,
|
||||||
GST_TIME_ARGS (GES_TIMELINE_ELEMENT_DURATION (self)),
|
GST_TIME_ARGS (GES_TIMELINE_ELEMENT_DURATION (self)),
|
||||||
GST_TIME_ARGS (duration));
|
GST_TIME_ARGS (duration));
|
||||||
|
|
||||||
|
klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
|
||||||
if (klass->set_duration) {
|
if (klass->set_duration) {
|
||||||
gint res = klass->set_duration (self, duration);
|
gint res = klass->set_duration (self, duration);
|
||||||
if (res == TRUE) {
|
if (res == TRUE && emit_notify) {
|
||||||
self->duration = duration;
|
self->duration = duration;
|
||||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DURATION]);
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DURATION]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,10 @@ struct _TreeIterationData
|
||||||
GHashTable *moved_clips;
|
GHashTable *moved_clips;
|
||||||
|
|
||||||
GList *neighbours;
|
GList *neighbours;
|
||||||
|
|
||||||
|
/* Data related to trimming groups */
|
||||||
|
GstClockTime trim_group_start;
|
||||||
|
GstClockTime trim_group_end;
|
||||||
} tree_iteration_data_init = {
|
} tree_iteration_data_init = {
|
||||||
.root = NULL,
|
.root = NULL,
|
||||||
.res = TRUE,
|
.res = TRUE,
|
||||||
|
@ -91,6 +95,8 @@ struct _TreeIterationData
|
||||||
.edge = GES_EDGE_NONE,
|
.edge = GES_EDGE_NONE,
|
||||||
.moved_clips = NULL,
|
.moved_clips = NULL,
|
||||||
.neighbours = NULL,
|
.neighbours = NULL,
|
||||||
|
.trim_group_start = GST_CLOCK_TIME_NONE,
|
||||||
|
.trim_group_end = GST_CLOCK_TIME_NONE,
|
||||||
};
|
};
|
||||||
/* *INDENT-ON* */
|
/* *INDENT-ON* */
|
||||||
|
|
||||||
|
@ -783,6 +789,47 @@ error:
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
trim_group_get_vals (TreeIterationData * data, GESTimelineElement * e,
|
||||||
|
GstClockTimeDiff * n_start, GstClockTimeDiff * n_inpoint,
|
||||||
|
GstClockTimeDiff * n_duration)
|
||||||
|
{
|
||||||
|
GstClockTimeDiff offset;
|
||||||
|
GstClockTimeDiff group_nstart =
|
||||||
|
GST_CLOCK_DIFF (data->start_diff, data->trim_group_start);
|
||||||
|
GstClockTimeDiff group_nend =
|
||||||
|
GST_CLOCK_DIFF (data->duration_diff, data->trim_group_end);
|
||||||
|
|
||||||
|
if (data->edge == GES_EDGE_START &&
|
||||||
|
((group_nstart >= e->start) || (e->start == data->trim_group_start))) {
|
||||||
|
|
||||||
|
offset = GST_CLOCK_DIFF (group_nstart, e->start);
|
||||||
|
*n_start = group_nstart;
|
||||||
|
*n_inpoint = GST_CLOCK_DIFF (offset, e->inpoint);
|
||||||
|
*n_duration =
|
||||||
|
GST_CLOCK_DIFF (*n_start, (GstClockTimeDiff) e->start + e->duration);
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (data->element, "Trimming %" GES_FORMAT " start",
|
||||||
|
GES_ARGS (e));
|
||||||
|
return TRUE;
|
||||||
|
} else if (data->edge == GES_EDGE_END &&
|
||||||
|
((group_nend <= _END (e)) || (_END (e) == data->trim_group_end))) {
|
||||||
|
|
||||||
|
offset = GST_CLOCK_DIFF (group_nend, _END (e));
|
||||||
|
*n_start = e->start;
|
||||||
|
*n_inpoint = e->inpoint;
|
||||||
|
*n_duration = GST_CLOCK_DIFF (offset, e->duration);
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (data->element, "Trimming %" GES_FORMAT " end",
|
||||||
|
GES_ARGS (e));
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ignoring child */
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
check_trim_child (GNode * node, TreeIterationData * data)
|
check_trim_child (GNode * node, TreeIterationData * data)
|
||||||
{
|
{
|
||||||
|
@ -793,6 +840,12 @@ check_trim_child (GNode * node, TreeIterationData * data)
|
||||||
GST_CLOCK_DIFF (data->duration_diff, e->duration) :
|
GST_CLOCK_DIFF (data->duration_diff, e->duration) :
|
||||||
GST_CLOCK_DIFF (n_start, (GstClockTimeDiff) e->start + e->duration);
|
GST_CLOCK_DIFF (n_start, (GstClockTimeDiff) e->start + e->duration);
|
||||||
|
|
||||||
|
if (GST_CLOCK_TIME_IS_VALID (data->trim_group_start)
|
||||||
|
&& !trim_group_get_vals (data, e, &n_start, &n_inpoint, &n_duration)) {
|
||||||
|
GST_DEBUG_OBJECT (data->element, "Not trimming");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
if (!timeline_tree_can_move_element_internal (data->root, e,
|
if (!timeline_tree_can_move_element_internal (data->root, e,
|
||||||
(gint64) ges_timeline_element_get_layer_priority (e) -
|
(gint64) ges_timeline_element_get_layer_priority (e) -
|
||||||
data->priority_diff, n_start, n_inpoint, n_duration, NULL,
|
data->priority_diff, n_start, n_inpoint, n_duration, NULL,
|
||||||
|
@ -823,21 +876,33 @@ timeline_tree_can_trim_element_internal (GNode * root, TreeIterationData * data)
|
||||||
|
|
||||||
static void
|
static void
|
||||||
trim_simple (GESTimelineElement * element, GstClockTimeDiff offset,
|
trim_simple (GESTimelineElement * element, GstClockTimeDiff offset,
|
||||||
GESEdge edge)
|
GESEdge edge, TreeIterationData * data)
|
||||||
{
|
{
|
||||||
|
GESTimelineElement *toplevel =
|
||||||
|
ges_timeline_element_get_toplevel_parent (element);
|
||||||
|
|
||||||
|
GstClockTimeDiff n_start = GST_CLOCK_DIFF (offset, element->start);
|
||||||
|
GstClockTimeDiff n_inpoint = GST_CLOCK_DIFF (offset, element->inpoint);
|
||||||
|
GstClockTimeDiff n_duration = edge == GES_EDGE_END
|
||||||
|
? GST_CLOCK_DIFF (offset, element->duration)
|
||||||
|
: element->duration + offset;
|
||||||
|
|
||||||
|
if (data && GST_CLOCK_TIME_IS_VALID (data->trim_group_start))
|
||||||
|
g_assert (trim_group_get_vals (data, element, &n_start, &n_inpoint,
|
||||||
|
&n_duration));
|
||||||
|
|
||||||
ELEMENT_SET_FLAG (element, GES_TIMELINE_ELEMENT_SET_SIMPLE);
|
ELEMENT_SET_FLAG (element, GES_TIMELINE_ELEMENT_SET_SIMPLE);
|
||||||
if (edge == GES_EDGE_END) {
|
ELEMENT_SET_FLAG (toplevel, GES_TIMELINE_ELEMENT_SET_SIMPLE);
|
||||||
ges_timeline_element_set_duration (element, GST_CLOCK_DIFF (offset,
|
if (edge != GES_EDGE_END) {
|
||||||
element->duration));
|
ges_timeline_element_set_start (element, n_start);
|
||||||
} else {
|
ges_timeline_element_set_inpoint (element, n_inpoint);
|
||||||
ges_timeline_element_set_start (element, GST_CLOCK_DIFF (offset,
|
|
||||||
element->start));
|
|
||||||
ges_timeline_element_set_inpoint (element, GST_CLOCK_DIFF (offset,
|
|
||||||
element->inpoint));
|
|
||||||
ges_timeline_element_set_duration (element, element->duration + offset);
|
|
||||||
}
|
}
|
||||||
|
ges_timeline_element_set_duration (element, n_duration);
|
||||||
|
|
||||||
GST_LOG ("Trimmed %" GES_FORMAT, GES_ARGS (element));
|
GST_LOG ("Trimmed %" GES_FORMAT, GES_ARGS (element));
|
||||||
ELEMENT_UNSET_FLAG (element, GES_TIMELINE_ELEMENT_SET_SIMPLE);
|
ELEMENT_UNSET_FLAG (element, GES_TIMELINE_ELEMENT_SET_SIMPLE);
|
||||||
|
ELEMENT_UNSET_FLAG (toplevel, GES_TIMELINE_ELEMENT_SET_SIMPLE);
|
||||||
|
gst_object_unref (toplevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define SET_TRIMMING_DATA(data, _edge, offset) G_STMT_START { \
|
#define SET_TRIMMING_DATA(data, _edge, offset) G_STMT_START { \
|
||||||
|
@ -845,6 +910,10 @@ trim_simple (GESTimelineElement * element, GstClockTimeDiff offset,
|
||||||
data.start_diff = (_edge) == GES_EDGE_END ? 0 : (offset); \
|
data.start_diff = (_edge) == GES_EDGE_END ? 0 : (offset); \
|
||||||
data.inpoint_diff = (_edge) == GES_EDGE_END ? 0 : (offset); \
|
data.inpoint_diff = (_edge) == GES_EDGE_END ? 0 : (offset); \
|
||||||
data.duration_diff = (_edge) == GES_EDGE_END ? (offset) : -(offset); \
|
data.duration_diff = (_edge) == GES_EDGE_END ? (offset) : -(offset); \
|
||||||
|
if (GES_IS_GROUP (data.element)) {\
|
||||||
|
data.trim_group_start = data.element->start;\
|
||||||
|
data.trim_group_end = _END (data.element); \
|
||||||
|
} \
|
||||||
} G_STMT_END
|
} G_STMT_END
|
||||||
|
|
||||||
|
|
||||||
|
@ -869,6 +938,10 @@ timeline_tree_trim (GNode * root, GESTimelineElement * element,
|
||||||
};
|
};
|
||||||
TreeIterationData data = tree_iteration_data_init;
|
TreeIterationData data = tree_iteration_data_init;
|
||||||
|
|
||||||
|
/* Make sure to check all children of clips */
|
||||||
|
if (GES_IS_TRACK_ELEMENT (element) && element->parent)
|
||||||
|
element = element->parent;
|
||||||
|
|
||||||
data.root = root;
|
data.root = root;
|
||||||
data.element = element;
|
data.element = element;
|
||||||
data.priority_diff =
|
data.priority_diff =
|
||||||
|
@ -876,13 +949,12 @@ timeline_tree_trim (GNode * root, GESTimelineElement * element,
|
||||||
new_layer_priority;
|
new_layer_priority;
|
||||||
data.snapping = snapping_distance ? &snapping : NULL;
|
data.snapping = snapping_distance ? &snapping : NULL;
|
||||||
data.moved_clips = g_hash_table_new (g_direct_hash, g_direct_equal);
|
data.moved_clips = g_hash_table_new (g_direct_hash, g_direct_equal);
|
||||||
|
|
||||||
SET_TRIMMING_DATA (data, edge, offset);
|
SET_TRIMMING_DATA (data, edge, offset);
|
||||||
GST_INFO ("%" GES_FORMAT " trimming %s with offset %" G_GINT64_FORMAT "",
|
GST_INFO ("%" GES_FORMAT " trimming %s with offset %" G_GINT64_FORMAT "",
|
||||||
GES_ARGS (element), edge == GES_EDGE_END ? "end" : "start", offset);
|
GES_ARGS (element), edge == GES_EDGE_END ? "end" : "start", offset);
|
||||||
g_node_traverse (find_node (root, element), G_IN_ORDER,
|
g_node_traverse (find_node (root, get_toplevel_container (element)),
|
||||||
G_TRAVERSE_LEAVES, -1, (GNodeTraverseFunc) add_element_to_list,
|
G_IN_ORDER, G_TRAVERSE_LEAVES, -1,
|
||||||
&data.movings);
|
(GNodeTraverseFunc) add_element_to_list, &data.movings);
|
||||||
|
|
||||||
if (!timeline_tree_can_trim_element_internal (root, &data)) {
|
if (!timeline_tree_can_trim_element_internal (root, &data)) {
|
||||||
GST_INFO ("Can not trim object.");
|
GST_INFO ("Can not trim object.");
|
||||||
|
@ -909,7 +981,7 @@ timeline_tree_trim (GNode * root, GESTimelineElement * element,
|
||||||
|
|
||||||
g_hash_table_iter_init (&iter, data.moved_clips);
|
g_hash_table_iter_init (&iter, data.moved_clips);
|
||||||
while (g_hash_table_iter_next (&iter, (gpointer *) & elem, NULL))
|
while (g_hash_table_iter_next (&iter, (gpointer *) & elem, NULL))
|
||||||
trim_simple (elem, offset, edge);
|
trim_simple (elem, offset, edge, &data);
|
||||||
|
|
||||||
timeline_tree_create_transitions (root, ges_timeline_find_auto_transition);
|
timeline_tree_create_transitions (root, ges_timeline_find_auto_transition);
|
||||||
timeline_update_transition (root->data);
|
timeline_update_transition (root->data);
|
||||||
|
@ -1155,9 +1227,9 @@ timeline_tree_roll (GNode * root, GESTimelineElement * element,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trim_simple (element, offset, edge);
|
trim_simple (element, offset, edge, NULL);
|
||||||
for (tmp = data.neighbours; tmp; tmp = tmp->next)
|
for (tmp = data.neighbours; tmp; tmp = tmp->next)
|
||||||
trim_simple (tmp->data, offset, data.edge);
|
trim_simple (tmp->data, offset, data.edge, NULL);
|
||||||
|
|
||||||
done:
|
done:
|
||||||
timeline_update_duration (root->data);
|
timeline_update_duration (root->data);
|
||||||
|
|
|
@ -1096,8 +1096,10 @@ _trim_transition (GESTimeline * timeline, GESTimelineElement * element,
|
||||||
GESLayer *layer = ges_timeline_get_layer (timeline,
|
GESLayer *layer = ges_timeline_get_layer (timeline,
|
||||||
GES_TIMELINE_ELEMENT_LAYER_PRIORITY (element));
|
GES_TIMELINE_ELEMENT_LAYER_PRIORITY (element));
|
||||||
|
|
||||||
if (!ges_layer_get_auto_transition (layer))
|
if (!ges_layer_get_auto_transition (layer)) {
|
||||||
goto fail;
|
gst_object_unref (layer);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
gst_object_unref (layer);
|
gst_object_unref (layer);
|
||||||
for (tmp = timeline->priv->auto_transitions; tmp; tmp = tmp->next) {
|
for (tmp = timeline->priv->auto_transitions; tmp; tmp = tmp->next) {
|
||||||
|
@ -1123,10 +1125,6 @@ _trim_transition (GESTimeline * timeline, GESTimelineElement * element,
|
||||||
}
|
}
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
fail:
|
|
||||||
gst_object_unref (layer);
|
|
||||||
return FALSE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -195,40 +195,67 @@ GST_START_TEST (test_move_group)
|
||||||
* |----------------------------------|
|
* |----------------------------------|
|
||||||
* | 7--------- 2----------|
|
* | 7--------- 2----------|
|
||||||
* layer1: | | clip1 | | clip2 |
|
* layer1: | | clip1 | | clip2 |
|
||||||
* | 22--------30 62----------|
|
* | 20--------30 60----------|
|
||||||
* |----------------------------------|
|
* |----------------------------------|
|
||||||
*/
|
*/
|
||||||
ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 12);
|
ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 12);
|
||||||
CHECK_OBJECT_PROPS (clip, 12, 2, 3);
|
CHECK_OBJECT_PROPS (clip, 12, 2, 3);
|
||||||
CHECK_OBJECT_PROPS (clip1, 22, 7, 8);
|
CHECK_OBJECT_PROPS (clip1, 20, 5, 10);
|
||||||
CHECK_OBJECT_PROPS (clip2, 62, 2, 48);
|
CHECK_OBJECT_PROPS (clip2, 60, 0, 50);
|
||||||
CHECK_OBJECT_PROPS (group, 12, 0, 98);
|
CHECK_OBJECT_PROPS (group, 12, 0, 98);
|
||||||
ASSERT_OBJECT_REFCOUNT (group, "2 ref for the timeline", 2);
|
ASSERT_OBJECT_REFCOUNT (group, "2 ref for the timeline", 2);
|
||||||
|
|
||||||
/* Setting the duration would lead to overlaps */
|
/* Setting the duration would lead to overlaps */
|
||||||
ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (group), 10);
|
fail_if (ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (group),
|
||||||
|
10));
|
||||||
CHECK_OBJECT_PROPS (clip, 12, 2, 3);
|
CHECK_OBJECT_PROPS (clip, 12, 2, 3);
|
||||||
CHECK_OBJECT_PROPS (clip1, 22, 7, 8);
|
CHECK_OBJECT_PROPS (clip1, 20, 5, 10);
|
||||||
CHECK_OBJECT_PROPS (clip2, 62, 2, 48);
|
CHECK_OBJECT_PROPS (clip2, 60, 0, 50);
|
||||||
CHECK_OBJECT_PROPS (group, 12, 0, 98);
|
CHECK_OBJECT_PROPS (group, 12, 0, 98);
|
||||||
ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (group), 100);
|
ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (group), 100);
|
||||||
CHECK_OBJECT_PROPS (clip, 12, 2, 3);
|
CHECK_OBJECT_PROPS (clip, 12, 2, 3);
|
||||||
CHECK_OBJECT_PROPS (clip1, 22, 7, 8);
|
CHECK_OBJECT_PROPS (clip1, 20, 5, 10);
|
||||||
CHECK_OBJECT_PROPS (clip2, 62, 2, 50);
|
CHECK_OBJECT_PROPS (clip2, 60, 0, 52);
|
||||||
CHECK_OBJECT_PROPS (group, 12, 0, 100);
|
CHECK_OBJECT_PROPS (group, 12, 0, 100);
|
||||||
|
|
||||||
ges_timeline_element_set_start (GES_TIMELINE_ELEMENT (group), 20);
|
ges_timeline_element_set_start (GES_TIMELINE_ELEMENT (group), 20);
|
||||||
CHECK_OBJECT_PROPS (clip, 20, 2, 3);
|
CHECK_OBJECT_PROPS (clip, 20, 2, 3);
|
||||||
CHECK_OBJECT_PROPS (clip1, 30, 7, 8);
|
CHECK_OBJECT_PROPS (clip1, 28, 5, 10);
|
||||||
CHECK_OBJECT_PROPS (clip2, 70, 2, 50);
|
CHECK_OBJECT_PROPS (clip2, 68, 0, 52);
|
||||||
CHECK_OBJECT_PROPS (group, 20, 0, 100);
|
CHECK_OBJECT_PROPS (group, 20, 0, 100);
|
||||||
|
|
||||||
|
/* Trim fails because clip inpoint would become negative */
|
||||||
fail_if (ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 10));
|
fail_if (ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 10));
|
||||||
CHECK_OBJECT_PROPS (clip, 20, 2, 3);
|
CHECK_OBJECT_PROPS (clip, 20, 2, 3);
|
||||||
CHECK_OBJECT_PROPS (clip1, 30, 7, 8);
|
CHECK_OBJECT_PROPS (clip1, 28, 5, 10);
|
||||||
CHECK_OBJECT_PROPS (clip2, 70, 2, 50);
|
CHECK_OBJECT_PROPS (clip2, 68, 0, 52);
|
||||||
CHECK_OBJECT_PROPS (group, 20, 0, 100);
|
CHECK_OBJECT_PROPS (group, 20, 0, 100);
|
||||||
|
|
||||||
|
fail_unless (ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 18));
|
||||||
|
CHECK_OBJECT_PROPS (clip, 18, 0, 5);
|
||||||
|
CHECK_OBJECT_PROPS (clip1, 28, 5, 10);
|
||||||
|
CHECK_OBJECT_PROPS (clip2, 68, 0, 52);
|
||||||
|
CHECK_OBJECT_PROPS (group, 18, 0, 102);
|
||||||
|
|
||||||
|
fail_unless (ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (clip),
|
||||||
|
17));
|
||||||
|
CHECK_OBJECT_PROPS (clip, 18, 0, 17);
|
||||||
|
CHECK_OBJECT_PROPS (clip1, 28, 5, 10);
|
||||||
|
CHECK_OBJECT_PROPS (clip2, 68, 0, 52);
|
||||||
|
CHECK_OBJECT_PROPS (group, 18, 0, 102);
|
||||||
|
|
||||||
|
fail_unless (ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 30));
|
||||||
|
CHECK_OBJECT_PROPS (clip, 30, 12, 5);
|
||||||
|
CHECK_OBJECT_PROPS (clip1, 30, 7, 8);
|
||||||
|
CHECK_OBJECT_PROPS (clip2, 68, 0, 52);
|
||||||
|
CHECK_OBJECT_PROPS (group, 30, 0, 90);
|
||||||
|
|
||||||
|
fail_unless (ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 25));
|
||||||
|
CHECK_OBJECT_PROPS (clip, 25, 7, 10);
|
||||||
|
CHECK_OBJECT_PROPS (clip1, 25, 2, 13);
|
||||||
|
CHECK_OBJECT_PROPS (clip2, 68, 0, 52);
|
||||||
|
CHECK_OBJECT_PROPS (group, 25, 0, 95);
|
||||||
|
|
||||||
ASSERT_OBJECT_REFCOUNT (group, "2 ref for the timeline", 2);
|
ASSERT_OBJECT_REFCOUNT (group, "2 ref for the timeline", 2);
|
||||||
check_destroyed (G_OBJECT (timeline), G_OBJECT (group), NULL);
|
check_destroyed (G_OBJECT (timeline), G_OBJECT (group), NULL);
|
||||||
gst_object_unref (asset);
|
gst_object_unref (asset);
|
||||||
|
|
|
@ -188,8 +188,8 @@ class GESSimpleTimelineTest(GESTest):
|
||||||
len(self.track_types))
|
len(self.track_types))
|
||||||
self.layer = self.timeline.append_layer()
|
self.layer = self.timeline.append_layer()
|
||||||
|
|
||||||
def add_clip(self, start, in_point, duration):
|
def add_clip(self, start, in_point, duration, asset_type=GES.TestClip):
|
||||||
clip = GES.TestClip()
|
clip = GES.Asset.request(asset_type, None).extract()
|
||||||
clip.props.start = start
|
clip.props.start = start
|
||||||
clip.props.in_point = in_point
|
clip.props.in_point = in_point
|
||||||
clip.props.duration = duration
|
clip.props.duration = duration
|
||||||
|
@ -197,11 +197,11 @@ class GESSimpleTimelineTest(GESTest):
|
||||||
|
|
||||||
return clip
|
return clip
|
||||||
|
|
||||||
def append_clip(self, layer=0):
|
def append_clip(self, layer=0, asset_type=GES.TestClip):
|
||||||
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.TestClip()
|
clip = GES.Asset.request(asset_type, None).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))
|
||||||
|
@ -215,6 +215,9 @@ class GESSimpleTimelineTest(GESTest):
|
||||||
for clip in layer.get_clips():
|
for clip in layer.get_clips():
|
||||||
layer_timings.append(
|
layer_timings.append(
|
||||||
(type(clip), clip.props.start, clip.props.duration))
|
(type(clip), clip.props.start, clip.props.duration))
|
||||||
|
for child in clip.get_children(True):
|
||||||
|
self.assertEqual(child.props.start, clip.props.start)
|
||||||
|
self.assertEqual(child.props.duration, clip.props.duration)
|
||||||
|
|
||||||
res.append(layer_timings)
|
res.append(layer_timings)
|
||||||
if topology != res:
|
if topology != res:
|
||||||
|
|
|
@ -505,6 +505,160 @@ class TestEditing(common.GESSimpleTimelineTest):
|
||||||
]
|
]
|
||||||
])
|
])
|
||||||
|
|
||||||
|
def test_illegal_effect_move(self):
|
||||||
|
c0 = self.append_clip()
|
||||||
|
self.append_clip()
|
||||||
|
self.assertTimelineTopology([
|
||||||
|
[
|
||||||
|
(GES.TestClip, 0, 10),
|
||||||
|
(GES.TestClip, 10, 10),
|
||||||
|
]
|
||||||
|
])
|
||||||
|
|
||||||
|
effect = GES.Effect.new("agingtv")
|
||||||
|
c0.add(effect)
|
||||||
|
self.assertTimelineTopology([
|
||||||
|
[
|
||||||
|
(GES.TestClip, 0, 10),
|
||||||
|
(GES.TestClip, 10, 10),
|
||||||
|
]
|
||||||
|
])
|
||||||
|
|
||||||
|
self.assertFalse(effect.set_start(10))
|
||||||
|
self.assertEqual(effect.props.start, 0)
|
||||||
|
self.assertTimelineTopology([
|
||||||
|
[
|
||||||
|
(GES.TestClip, 0, 10),
|
||||||
|
(GES.TestClip, 10, 10),
|
||||||
|
]
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
self.assertFalse(effect.set_duration(20))
|
||||||
|
self.assertEqual(effect.props.duration, 10)
|
||||||
|
self.assertTimelineTopology([
|
||||||
|
[
|
||||||
|
(GES.TestClip, 0, 10),
|
||||||
|
(GES.TestClip, 10, 10),
|
||||||
|
]
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_moving_overlay_clip_in_group(self):
|
||||||
|
c0 = self.append_clip()
|
||||||
|
overlay = self.append_clip(asset_type=GES.TextOverlayClip)
|
||||||
|
group = GES.Group.new()
|
||||||
|
group.add(c0)
|
||||||
|
group.add(overlay)
|
||||||
|
|
||||||
|
self.assertTimelineTopology([
|
||||||
|
[
|
||||||
|
(GES.TestClip, 0, 10),
|
||||||
|
(GES.TextOverlayClip, 10, 10),
|
||||||
|
]
|
||||||
|
], groups=[(c0, overlay)])
|
||||||
|
|
||||||
|
self.assertTrue(overlay.set_start(20))
|
||||||
|
self.assertTimelineTopology([
|
||||||
|
[
|
||||||
|
(GES.TestClip, 10, 10),
|
||||||
|
(GES.TextOverlayClip, 20, 10),
|
||||||
|
]
|
||||||
|
], groups=[(c0, overlay)])
|
||||||
|
|
||||||
|
def test_moving_group_in_group(self):
|
||||||
|
c0 = self.append_clip()
|
||||||
|
overlay = self.append_clip(asset_type=GES.TextOverlayClip)
|
||||||
|
group0 = GES.Group.new()
|
||||||
|
group0.add(c0)
|
||||||
|
group0.add(overlay)
|
||||||
|
|
||||||
|
c1 = self.append_clip()
|
||||||
|
group1 = GES.Group.new()
|
||||||
|
group1.add(group0)
|
||||||
|
group1.add(c1)
|
||||||
|
|
||||||
|
self.assertTimelineTopology([
|
||||||
|
[
|
||||||
|
(GES.TestClip, 0, 10),
|
||||||
|
(GES.TextOverlayClip, 10, 10),
|
||||||
|
(GES.TestClip, 20, 10),
|
||||||
|
]
|
||||||
|
], groups=[(c1, group0), (c0, overlay)])
|
||||||
|
|
||||||
|
self.assertTrue(group0.set_start(10))
|
||||||
|
self.assertTimelineTopology([
|
||||||
|
[
|
||||||
|
(GES.TestClip, 10, 10),
|
||||||
|
(GES.TextOverlayClip, 20, 10),
|
||||||
|
(GES.TestClip, 30, 10),
|
||||||
|
]
|
||||||
|
], groups=[(c1, group0), (c0, overlay)])
|
||||||
|
self.check_element_values(group0, 10, 0, 20)
|
||||||
|
self.check_element_values(group1, 10, 0, 30)
|
||||||
|
|
||||||
|
def test_illegal_group_child_move(self):
|
||||||
|
clip0 = self.append_clip()
|
||||||
|
_clip1 = self.add_clip(20, 0, 10)
|
||||||
|
overlay = self.add_clip(20, 0, 10, asset_type=GES.TextOverlayClip)
|
||||||
|
|
||||||
|
group = GES.Group.new()
|
||||||
|
group.add(clip0)
|
||||||
|
group.add(overlay)
|
||||||
|
|
||||||
|
self.assertTimelineTopology([
|
||||||
|
[
|
||||||
|
(GES.TestClip, 0, 10),
|
||||||
|
(GES.TextOverlayClip, 20, 10),
|
||||||
|
(GES.TestClip, 20, 10),
|
||||||
|
]
|
||||||
|
], groups=[(clip0, overlay),])
|
||||||
|
|
||||||
|
# Can't move as clip0 and clip1 would fully overlap
|
||||||
|
self.assertFalse(overlay.set_start(40))
|
||||||
|
self.assertTimelineTopology([
|
||||||
|
[
|
||||||
|
(GES.TestClip, 0, 10),
|
||||||
|
(GES.TextOverlayClip, 20, 10),
|
||||||
|
(GES.TestClip, 20, 10),
|
||||||
|
]
|
||||||
|
], groups=[(clip0, overlay)])
|
||||||
|
|
||||||
|
def test_child_duration_change(self):
|
||||||
|
c0 = self.append_clip()
|
||||||
|
|
||||||
|
self.assertTimelineTopology([
|
||||||
|
[
|
||||||
|
(GES.TestClip, 0, 10),
|
||||||
|
]
|
||||||
|
])
|
||||||
|
self.assertTrue(c0.set_duration(40))
|
||||||
|
self.assertTimelineTopology([
|
||||||
|
[
|
||||||
|
(GES.TestClip, 0, 40),
|
||||||
|
]
|
||||||
|
])
|
||||||
|
|
||||||
|
c0.children[0].set_duration(10)
|
||||||
|
self.assertTimelineTopology([
|
||||||
|
[
|
||||||
|
(GES.TestClip, 0, 10),
|
||||||
|
]
|
||||||
|
])
|
||||||
|
|
||||||
|
self.assertTrue(c0.set_start(40))
|
||||||
|
self.assertTimelineTopology([
|
||||||
|
[
|
||||||
|
(GES.TestClip, 40, 10),
|
||||||
|
]
|
||||||
|
])
|
||||||
|
|
||||||
|
c0.children[0].set_start(10)
|
||||||
|
self.assertTimelineTopology([
|
||||||
|
[
|
||||||
|
(GES.TestClip, 10, 10),
|
||||||
|
]
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
class TestInvalidOverlaps(common.GESSimpleTimelineTest):
|
class TestInvalidOverlaps(common.GESSimpleTimelineTest):
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue