timeline-tree: simplify and fix editing

Editing has been simplified by breaking down each edit into a
combination of three basic single-element edits: MOVE, TRIM_START, and
TRIM_END.

Each edit follows these steps:
+ Determine which elements are to be edited and under which basic mode
+ Determine which track elements will move as a result
+ Snap the edit position to one of the edges of the main edited element,
  (or the edge of one of its descendants, in the case of MOVE), avoiding
  moving elements.
  NOTE: in particular, we can *not* snap to the edge of a neighbouring
  element in a roll edit. This was previously possible, even though the
  neighbour was moving!
+ Determine the edit positions for clips (or track elements with no
  parent) using the snapped value. In addition, we replace any edits of
  a group with an edit of its descendant clips. If any value would be
  out of bounds (e.g. negative start) we do not edit.
  NOTE: this is now done *after* checking the snapping. This allows the
  edit to succeed if snapping would cause it to go from being invalid to
  valid!
+ Determine whether the collection of edits would result in a valid
  timeline-configuration which does not break the rules for sources
  overlapping.
+ If all this succeeds, we emit snapping-started on the timeline.
+ We then perform all the edits. At this point they should all succeed.

The simplification/unification should make it easier to make other
changes.

Fixes https://gitlab.freedesktop.org/gstreamer/gst-editing-services/-/issues/97
Fixes https://gitlab.freedesktop.org/gstreamer/gst-editing-services/-/issues/98

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-editing-services/-/merge_requests/169>
This commit is contained in:
Henry Wilkes 2020-04-27 13:58:38 +01:00
parent a6b13ce619
commit 39097f5574
14 changed files with 1928 additions and 1213 deletions

View file

@ -296,7 +296,8 @@ _update_duration_limit (GESClip * self)
GST_INFO_OBJECT (self, "duration-limit for the clip is %" GST_INFO_OBJECT (self, "duration-limit for the clip is %"
GST_TIME_FORMAT, GST_TIME_ARGS (duration_limit)); GST_TIME_FORMAT, GST_TIME_ARGS (duration_limit));
if (_CLOCK_TIME_IS_LESS (duration_limit, element->duration)) { if (_CLOCK_TIME_IS_LESS (duration_limit, element->duration)
&& !ELEMENT_FLAG_IS_SET (self, GES_TIMELINE_ELEMENT_SET_SIMPLE)) {
gboolean res; gboolean res;
GST_INFO_OBJECT (self, "Automatically reducing duration to %" GST_INFO_OBJECT (self, "Automatically reducing duration to %"
@ -1849,11 +1850,12 @@ ges_clip_move_to_layer (GESClip * clip, GESLayer * layer)
{ {
gboolean ret = FALSE; gboolean ret = FALSE;
GESLayer *current_layer; GESLayer *current_layer;
GESTimeline *current_timeline = GES_TIMELINE_ELEMENT_TIMELINE (clip); GESTimelineElement *element;
g_return_val_if_fail (GES_IS_CLIP (clip), FALSE); g_return_val_if_fail (GES_IS_CLIP (clip), FALSE);
g_return_val_if_fail (GES_IS_LAYER (layer), FALSE); g_return_val_if_fail (GES_IS_LAYER (layer), FALSE);
element = GES_TIMELINE_ELEMENT (clip);
current_layer = clip->priv->layer; current_layer = clip->priv->layer;
if (current_layer == layer) { if (current_layer == layer) {
@ -1861,39 +1863,34 @@ ges_clip_move_to_layer (GESClip * clip, GESLayer * layer)
return TRUE; return TRUE;
} }
gst_object_ref (clip);
if (current_layer == NULL) { if (current_layer == NULL) {
GST_DEBUG ("Not moving %p, only adding it to %p", clip, layer); GST_DEBUG ("Not moving %p, only adding it to %p", clip, layer);
ret = ges_layer_add_clip (layer, clip); return ges_layer_add_clip (layer, clip);
goto done;
} }
ELEMENT_SET_FLAG (clip, GES_CLIP_IS_MOVING); if (element->timeline != layer->timeline) {
if (current_timeline != layer->timeline) {
/* make sure we can perform the can_move_element_check in the timeline /* make sure we can perform the can_move_element_check in the timeline
* of the layer */ * of the layer */
GST_WARNING_OBJECT (layer, "Cannot move clip %" GES_FORMAT " into " GST_WARNING_OBJECT (layer, "Cannot move clip %" GES_FORMAT " into "
"the layer because its timeline %" GST_PTR_FORMAT " does not " "the layer because its timeline %" GST_PTR_FORMAT " does not "
"match the timeline of the layer %" GST_PTR_FORMAT, "match the timeline of the layer %" GST_PTR_FORMAT,
GES_ARGS (clip), current_timeline, layer->timeline); GES_ARGS (clip), element->timeline, layer->timeline);
ret = FALSE; return FALSE;
goto done;
} }
if (layer->timeline if (layer->timeline
&& !timeline_tree_can_move_element (timeline_get_tree (layer->timeline), && !ELEMENT_FLAG_IS_SET (clip, GES_TIMELINE_ELEMENT_SET_SIMPLE)) {
GES_TIMELINE_ELEMENT (clip), /* move to new layer, also checks moving of toplevel */
ges_layer_get_priority (layer), return timeline_tree_move (timeline_get_tree (layer->timeline),
GES_TIMELINE_ELEMENT_START (clip), element, (gint64) ges_layer_get_priority (current_layer) -
GES_TIMELINE_ELEMENT_DURATION (clip), NULL)) { (gint64) ges_layer_get_priority (layer), 0, GES_EDGE_NONE, 0);
GST_INFO_OBJECT (layer, "Clip %" GES_FORMAT " can't move to layer %d",
GES_ARGS (clip), ges_layer_get_priority (layer));
goto done;
} }
gst_object_ref (clip);
ELEMENT_SET_FLAG (clip, GES_CLIP_IS_MOVING);
GST_DEBUG_OBJECT (clip, "moving to layer %p, priority: %d", layer, GST_DEBUG_OBJECT (clip, "moving to layer %p, priority: %d", layer,
ges_layer_get_priority (layer)); ges_layer_get_priority (layer));
@ -2250,7 +2247,7 @@ ges_clip_split (GESClip * clip, guint64 position)
if (timeline && !timeline_tree_can_move_element (timeline_get_tree if (timeline && !timeline_tree_can_move_element (timeline_get_tree
(timeline), element, (timeline), element,
ges_timeline_element_get_layer_priority (element), ges_timeline_element_get_layer_priority (element),
start, old_duration, NULL)) { start, old_duration)) {
GST_WARNING_OBJECT (clip, GST_WARNING_OBJECT (clip,
"Can not split %" GES_FORMAT " at %" GST_TIME_FORMAT "Can not split %" GES_FORMAT " at %" GST_TIME_FORMAT
" as timeline would be in an illegal" " state.", GES_ARGS (clip), " as timeline would be in an illegal" " state.", GES_ARGS (clip),
@ -2262,7 +2259,7 @@ ges_clip_split (GESClip * clip, guint64 position)
if (timeline && !timeline_tree_can_move_element (timeline_get_tree if (timeline && !timeline_tree_can_move_element (timeline_get_tree
(timeline), element, (timeline), element,
ges_timeline_element_get_layer_priority (element), ges_timeline_element_get_layer_priority (element),
position, new_duration, NULL)) { position, new_duration)) {
GST_WARNING_OBJECT (clip, GST_WARNING_OBJECT (clip,
"Can not split %" GES_FORMAT " at %" GST_TIME_FORMAT "Can not split %" GES_FORMAT " at %" GST_TIME_FORMAT
" as timeline would end up in an illegal" " state.", GES_ARGS (clip), " as timeline would end up in an illegal" " state.", GES_ARGS (clip),

View file

@ -144,6 +144,25 @@ register_ges_edit_mode (GType * id)
*id = g_enum_register_static ("GESEditMode", edit_mode); *id = g_enum_register_static ("GESEditMode", edit_mode);
} }
const gchar *
ges_edit_mode_name (GESEditMode mode)
{
switch (mode) {
case GES_EDIT_MODE_NORMAL:
return "normal";
case GES_EDIT_MODE_RIPPLE:
return "ripple";
case GES_EDIT_MODE_ROLL:
return "roll";
case GES_EDIT_MODE_TRIM:
return "trim";
case GES_EDIT_MODE_SLIDE:
return "slide";
default:
return "unknown";
}
}
GType GType
ges_edit_mode_get_type (void) ges_edit_mode_get_type (void)
{ {

View file

@ -365,73 +365,150 @@ GType ges_pipeline_flags_get_type (void);
/** /**
* GESEditMode: * GESEditMode:
* @GES_EDIT_MODE_NORMAL: The element is edited the normal way (default). * @GES_EDIT_MODE_NORMAL: The element is edited the normal way (default).
* This only moves a single element. If acting on the start edge of the * If acting on the element as a whole (#GES_EDGE_NONE), this will MOVE
* element, the element's start time is set to the edit position. * the element by MOVING its toplevel. When acting on the start of the
* If acting on end edge of the element, the element's duration time * element (#GES_EDGE_START), this will only MOVE the element, but not
* is set such that its end time matches the edit position. * its toplevel parent. This can allow you to move a #GESClip or
* @GES_EDIT_MODE_RIPPLE: The element is edited in ripple mode. This * #GESGroup to a new start time or layer within its container group,
* shifts the element and all later elements (those with equal or later * without effecting other members of the group. When acting on the end
* start times) in the timeline by the same amount. If acting on the * of the element (#GES_EDGE_END), this will END-TRIM the element,
* element as a whole, the element's start time is shifted to the edit * leaving its toplevel unchanged.
* position, and later elements are also shifted by the same amount. If * @GES_EDIT_MODE_RIPPLE: The element is edited in ripple mode: moving
* acting on the end edge of the element, the element's **duration time** * itself as well as later elements, keeping their relative times. This
* is shifted so that the element's end time matches the edit position, * edits the element the same as #GES_EDIT_MODE_NORMAL. In addition, if
* and later elements have their **start time** shifted by the same * acting on the element as a whole, or the start of the element, any
* amount. * toplevel element in the same timeline (including different layers)
* @GES_EDIT_MODE_ROLL: The element is edited in roll mode. This trims * whose start time is later than the *current* start time of the MOVED
* the edge of an element and neighbouring edges (opposite edges of other * element will also be MOVED by the same shift as the edited element.
* elements in the timeline with the same corresponding time value), such * If acting on the end of the element, any toplevel element whose start
* that the edges remain in contact. If acting on the start edge of the * time is later than the *current* end time of the edited element will
* element, the start edge is trimmed to the edit position (see * also be MOVED by the same shift as the change in the end of the
* #GES_EDIT_MODE_TRIM), and any other elements in the timeline whose end * edited element. These additional elements will also be shifted by
* time matches the edited element's start time (evaluated before the * the same shift in layers as the edited element.
* edit) will have their **end** edge trimmed to the same edit position. * @GES_EDIT_MODE_ROLL: The element is edited in roll mode: swapping its
* Similarly, if acting on the end edge of the element, the end edge is * content for its neighbour's, or vis versa, in the timeline output.
* trimmed to the edit position, and any other elements in the timeline * This edits the element the same as #GES_EDIT_MODE_TRIM. In addition,
* whose start time matches the edited element's end time will have * any neighbours are also TRIMMED at their opposite edge to the same
* their start edge trimmed to the same edit position. * timeline position. When acting on the start of the element, a
* @GES_EDIT_MODE_TRIM: The element is edited in trim mode. This shifts * neighbour is any earlier element in the timeline whose end time
* the edge of a single element while maintaining the timing of * matches the *current* start time of the edited element. When acting on
* its internal content in the timeline, so the samples/frames/etc of a * the end of the element, a neighbour is any later element in the
* source would still appear at the same timeline time when it is played. * timeline whose start time matches the *current* start time of the
* If acting on the start edge of the element, the element's start time * edited element. In addition, a neighbour have a #GESSource at its
* will be shifted to the edit position and the element's in-point time * end/start edge that shares a track with a #GESSource at the start/end
* will be shifted by the same amount. Additionally, the element's * edge of the edited element. Basically, a neighbour is an element that
* duration time will be shifted the other way such that the element's * can be extended, or cut, to have its content replace, or be replaced
* end time remains the same. If acting on end edge of the element, the * by, the content of the edited element. Acting on the element as a
* element's duration time is set such that its end time matches the edit * whole (#GES_EDGE_NONE) is not defined. The element can not shift
* position. * layers under this mode.
* @GES_EDIT_MODE_TRIM: The element is edited in trim mode. When acting
* on the start of the element, this will START-TRIM it. When acting on
* the end of the element, this will END-TRIM it. Acting on the element
* as a whole (#GES_EDGE_NONE) is not defined.
* @GES_EDIT_MODE_SLIDE: The element is edited in slide mode (not yet * @GES_EDIT_MODE_SLIDE: The element is edited in slide mode (not yet
* implemented). This shifts the element and will trim the edges of * implemented): moving the element replacing or consuming content on
* neighbouring edges on either side accordingly. If acting on the * each end. When acting on the element as a whole, this will MOVE the
* element as a whole, the element's start time is shifted to the edit * element, and TRIM any neighbours on either side. A neighbour is
* position. Any other elements in the timeline whose end time matches * defined in the same way as in #GES_EDIT_MODE_ROLL, but they may be on
* the edited element's start time (evaluated before the edit) will have * either side of the edited elements. Elements at the end with be
* their end edge trimmed to the same edit position. Additionally, any * START-TRIMMED to the new end position of the edited element. Elements
* other elements in the timeline whose start time matches the edited * at the start will be END-TRIMMED to the new start position of the
* element's end time will have their start edge trimmed to match the * edited element. Acting on the start or end of the element
* edited element's **new** end time. * (#GES_EDGE_START and #GES_EDGE_END) is not defined. The element can
* not shift layers under this mode.
* *
* When a single timeline element is edited within its timeline, using * When a single timeline element is edited within its timeline at some
* ges_timeline_element_edit(), depending on the edit mode, its * position, using ges_timeline_element_edit(), depending on the edit
* #GESTimelineElement:start, #GESTimelineElement:duration or * mode, its #GESTimelineElement:start, #GESTimelineElement:duration or
* #GESTimelineElement:in-point will be adjusted accordingly. In addition, * #GESTimelineElement:in-point will be adjusted accordingly. In addition,
* other elements in the timeline may also have their properties adjusted. * any clips may change #GESClip:layer.
* *
* In fact, the edit is actually performed on the toplevel of the edited * Each edit can be broken down into a combination of three basic edits:
* element (usually a #GESClip), which is responsible for moving its
* children along with it. For simplicity, in the descriptions we will
* use "element" to exclusively refer to toplevel elements.
* *
* In the edit mode descriptions, the "start edge", "end edge" and the * + MOVE: This moves the start of the element to the edit position.
* "element as a whole" correspond to using #GES_EDGE_START, #GES_EDGE_END * + START-TRIM: This cuts or grows the start of the element, whilst
* and #GES_EDGE_NONE as part of the edit, respectively. The "start time", * maintaining the time at which its internal content appears in the
* "duration time" and "in-point time" correspond to the * timeline data output. If the element is made shorter, the data that
* #GESTimelineElement:start, #GESTimelineElement:duration and * appeared at the edit position and later will still appear in the
* #GESTimelineElement:in-point properties, respectively. Moreover, the * timeline at the same time. If the element is made longer, the data
* "end time" refers to the final time of the element: * that appeared at the previous start of the element and later will
* #GESTimelineElement:start + #GESTimelineElement:duration. Finally, * still appear in the timeline at the same time.
* the "edit position" is the timeline time used as part of the edit. * + END-TRIM: Similar to START-TRIM, but the end of the element is cut or
* grown.
*
* In particular, when editing a #GESClip:
*
* + MOVE: This will set the #GESTimelineElement:start of the clip to the
* edit position.
* + START-TRIM: This will set the #GESTimelineElement:start of the clip
* to the edit position. To keep the end time the same, the
* #GESTimelineElement:duration of the clip will be adjusted in the
* opposite direction. In addition, the #GESTimelineElement:in-point of
* the clip will be shifted such that the content that appeared at the
* new or previous start time, whichever is latest, still appears at the
* same timeline time. For example, if a frame appeared at the start of
* the clip, and the start of the clip is reduced, the in-point of the
* clip will also reduce such that the frame will appear later within
* the clip, but at the same timeline position.
* + END-TRIM: This will set the #GESTimelineElement:duration of the clip
* such that its end time will match the edit position.
*
* When editing a #GESGroup:
*
* + MOVE: This will set the #GESGroup:start of the clip to the edit
* position by shifting all of its children by the same amount. So each
* child will maintain their relative positions.
* + START-TRIM: If the group is made shorter, this will START-TRIM any
* clips under the group that start after the edit position to the same
* edit position. If the group is made longer, this will START-TRIM any
* clip under the group whose start matches the start of the group to
* the same edit position.
* + END-TRIM: If the group is made shorter, this will END-TRIM any clips
* under the group that end after the edit position to the same edit
* position. If the group is made longer, this will END-TRIM any clip
* under the group whose end matches the end of the group to the same
* edit position.
*
* When editing a #GESTrackElement, if it has a #GESClip parent, this
* will be edited instead. Otherwise it is edited in the same way as a
* #GESClip.
*
* The layer priority of a #GESGroup is the lowest layer priority of any
* #GESClip underneath it. When a group is edited to a new layer
* priority, it will shift all clips underneath it by the same amount,
* such that their relative layers stay the same.
*
* If the #GESTimeline has a #GESTimeline:snapping-distance, then snapping
* may occur for some of the edges of the **main** edited element:
*
* + MOVE: The start or end edge of *any* #GESSource under the element may
* be snapped.
* + START-TRIM: The start edge of a #GESSource whose start edge touches
* the start edge of the element may snap.
* + END-TRIM: The end edge of a #GESSource whose end edge touches the end
* edge of the element may snap.
*
* These edges may snap with either the start or end edge of *any* other
* #GESSource in the timeline that is not also being moved by the element,
* including those in different layers, if they are within the
* #GESTimeline:snapping-distance. During an edit, only up to one snap can
* occur. This will shift the edit position such that the snapped edges
* will touch once the edit has completed.
*
* Note that snapping can cause an edit to fail where it would have
* otherwise succeeded because it may push the edit position such that the
* edit would result in an unsupported timeline configuration. Similarly,
* snapping can cause an edit to succeed where it would have otherwise
* failed.
*
* For example, in #GES_EDIT_MODE_RIPPLE acting on #GES_EDGE_NONE, the
* main element is the MOVED toplevel of the edited element. Any source
* under the main MOVED toplevel may have its start or end edge snapped.
* Note, these sources cannot snap with each other. The edit may also
* push other elements, but any sources under these elements cannot snap,
* nor can they be snapped with. If a snap does occur, the MOVE of the
* toplevel *and* all other elements pushed by the ripple will be shifted
* by the same amount such that the snapped edges will touch.
* *
* You can also find more explanation about the behaviour of those modes at: * You can also find more explanation about the behaviour of those modes at:
* [trim, ripple and roll](http://pitivi.org/manual/trimming.html) * [trim, ripple and roll](http://pitivi.org/manual/trimming.html)
@ -445,6 +522,9 @@ typedef enum {
GES_EDIT_MODE_SLIDE GES_EDIT_MODE_SLIDE
} GESEditMode; } GESEditMode;
GES_API
const gchar * ges_edit_mode_name (GESEditMode mode);
#define GES_TYPE_EDIT_MODE ges_edit_mode_get_type() #define GES_TYPE_EDIT_MODE ges_edit_mode_get_type()
GES_API GES_API

View file

@ -436,12 +436,6 @@ _set_duration (GESTimelineElement * element, GstClockTime duration)
return GES_TIMELINE_ELEMENT_CLASS (parent_class)->set_duration (element, return GES_TIMELINE_ELEMENT_CLASS (parent_class)->set_duration (element,
duration); duration);
if (element->timeline
&& !timeline_tree_can_move_element (timeline_get_tree (element->timeline),
element, _PRIORITY (element), element->start, duration, NULL)) {
return FALSE;
}
if (container->initiated_move == NULL) { if (container->initiated_move == NULL) {
gboolean expending = (_DURATION (element) < duration); gboolean expending = (_DURATION (element) < duration);

View file

@ -88,37 +88,11 @@ GstDebugCategory * _ges_debug (void);
#define SUPRESS_UNUSED_WARNING(a) (void)a #define SUPRESS_UNUSED_WARNING(a) (void)a
G_GNUC_INTERNAL gboolean
timeline_ripple_object (GESTimeline *timeline, GESTimelineElement *obj,
gint new_layer_priority,
GList * layers, GESEdge edge,
guint64 position);
G_GNUC_INTERNAL gboolean G_GNUC_INTERNAL gboolean
timeline_slide_object (GESTimeline *timeline, GESTrackElement *obj, ges_timeline_edit (GESTimeline * timeline, GESTimelineElement * element,
GList * layers, GESEdge edge, guint64 position); GList * layers, gint64 new_layer_priority, GESEditMode mode, GESEdge edge,
guint64 position);
G_GNUC_INTERNAL gboolean
timeline_roll_object (GESTimeline *timeline, GESTimelineElement *obj,
GList * layers, GESEdge edge, guint64 position);
G_GNUC_INTERNAL gboolean
timeline_trim_object (GESTimeline *timeline, GESTimelineElement * object,
guint32 new_layer_priority, GList * layers, GESEdge edge,
guint64 position);
G_GNUC_INTERNAL gboolean
ges_timeline_trim_object_simple (GESTimeline * timeline, GESTimelineElement * obj,
guint32 new_layer_priority, GList * layers, GESEdge edge,
guint64 position, gboolean snapping);
G_GNUC_INTERNAL gboolean
ges_timeline_move_object_simple (GESTimeline * timeline, GESTimelineElement * object,
GList * layers, GESEdge edge, guint64 position);
G_GNUC_INTERNAL gboolean
timeline_move_object (GESTimeline *timeline, GESTimelineElement * object,
guint32 new_layer_priority, GList * layers, GESEdge edge,
guint64 position);
G_GNUC_INTERNAL void G_GNUC_INTERNAL void
timeline_add_group (GESTimeline *timeline, timeline_add_group (GESTimeline *timeline,

View file

@ -1045,21 +1045,21 @@ ges_timeline_element_get_timeline (GESTimelineElement * self)
* @self: A #GESTimelineElement * @self: A #GESTimelineElement
* @start: The desired start position of the element in its timeline * @start: The desired start position of the element in its timeline
* *
* Sets #GESTimelineElement:start for the element. This may fail if it * Sets #GESTimelineElement:start for the element. If the element has a
* would place the timeline in an unsupported configuration. * parent, this will also move its siblings with the same shift.
* *
* Note that if the element's timeline has a * Whilst the element is part of a #GESTimeline, this is the same as
* #GESTimeline:snapping-distance set, then the element's * editing the element with ges_timeline_element_edit() under
* #GESTimelineElement:start may instead be set to the edge of some other * #GES_EDIT_MODE_NORMAL with #GES_EDGE_NONE. In particular, the
* element in the neighbourhood of @start. In such a case, the return * #GESTimelineElement:start of the element may be snapped to a different
* value will still be %TRUE on success. * timeline time from the one given. In addition, setting may fail if it
* would place the timeline in an unsupported configuration.
* *
* Returns: %TRUE if @start could be set for @self. * Returns: %TRUE if @start could be set for @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;
@ -1078,18 +1078,18 @@ ges_timeline_element_set_start (GESTimelineElement * self, GstClockTime start)
&& !ELEMENT_FLAG_IS_SET (self, GES_TIMELINE_ELEMENT_SET_SIMPLE) && !ELEMENT_FLAG_IS_SET (self, GES_TIMELINE_ELEMENT_SET_SIMPLE)
&& !ELEMENT_FLAG_IS_SET (toplevel_container, && !ELEMENT_FLAG_IS_SET (toplevel_container,
GES_TIMELINE_ELEMENT_SET_SIMPLE)) { 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; gst_object_unref (toplevel_container);
return ges_timeline_element_edit (self, NULL, -1, GES_EDIT_MODE_NORMAL,
GES_EDGE_NONE, start);
} }
parent = self->parent; parent = self->parent;
/* FIXME This should not belong to GESTimelineElement */ /* FIXME This should not belong to GESTimelineElement */
if (toplevel_container && /* only check if no timeline, otherwise the timeline-tree will handle this
* check */
if (!self->timeline && toplevel_container &&
((gint64) (_START (toplevel_container) + start - _START (self))) < 0 && ((gint64) (_START (toplevel_container) + start - _START (self))) < 0 &&
parent parent
&& GES_CONTAINER (parent)->children_control_mode == GES_CHILDREN_UPDATE) { && GES_CONTAINER (parent)->children_control_mode == GES_CHILDREN_UPDATE) {
@ -1107,7 +1107,7 @@ ges_timeline_element_set_start (GESTimelineElement * self, GstClockTime start)
gint res = klass->set_start (self, start); gint res = klass->set_start (self, start);
if (res == FALSE) if (res == FALSE)
return FALSE; return FALSE;
if (res == TRUE && emit_notify) { if (res == TRUE) {
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]);
} }
@ -1238,17 +1238,16 @@ ges_timeline_element_set_max_duration (GESTimelineElement * self,
* @self: A #GESTimelineElement * @self: A #GESTimelineElement
* @duration: The desired duration in its timeline * @duration: The desired duration in its timeline
* *
* Sets #GESTimelineElement:duration for the element. This may fail if it * Sets #GESTimelineElement:duration for the element.
* would place the timeline in an unsupported configuration, or if the
* element does not have enough internal content to last for the desired
* @duration.
* *
* Note that if the element's timeline has a * Whilst the element is part of a #GESTimeline, this is the same as
* #GESTimeline:snapping-distance set, then the element's * editing the element with ges_timeline_element_edit() under
* #GESTimelineElement:duration may instead be adjusted around @duration * #GES_EDIT_MODE_TRIM with #GES_EDGE_END. In particular, the
* such that the edge of @self matches the edge of some other element in * #GESTimelineElement:duration of the element may be snapped to a
* the neighbourhood. In such a case, the return value will still be %TRUE * different timeline time difference from the one given. In addition,
* on success. * setting may fail if it would place the timeline in an unsupported
* configuration, or the element does not have enough internal content to
* last the desired duration.
* *
* Returns: %TRUE if @duration could be set for @self. * Returns: %TRUE if @duration could be set for @self.
*/ */
@ -1257,7 +1256,6 @@ ges_timeline_element_set_duration (GESTimelineElement * self,
GstClockTime duration) GstClockTime duration)
{ {
GESTimelineElementClass *klass; GESTimelineElementClass *klass;
gboolean emit_notify = TRUE;
GESTimelineElement *toplevel; GESTimelineElement *toplevel;
g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE); g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
@ -1269,19 +1267,9 @@ ges_timeline_element_set_duration (GESTimelineElement * self,
if (self->timeline && if (self->timeline &&
!ELEMENT_FLAG_IS_SET (self, GES_TIMELINE_ELEMENT_SET_SIMPLE) && !ELEMENT_FLAG_IS_SET (self, GES_TIMELINE_ELEMENT_SET_SIMPLE) &&
!ELEMENT_FLAG_IS_SET (toplevel, GES_TIMELINE_ELEMENT_SET_SIMPLE)) { !ELEMENT_FLAG_IS_SET (toplevel, GES_TIMELINE_ELEMENT_SET_SIMPLE)) {
gboolean res; gst_object_unref (toplevel);
return ges_timeline_element_edit (self, NULL, -1, GES_EDIT_MODE_TRIM,
res = timeline_trim_object (self->timeline, self,
GES_TIMELINE_ELEMENT_LAYER_PRIORITY (self), NULL,
GES_EDGE_END, self->start + duration); GES_EDGE_END, self->start + duration);
if (!res) {
gst_object_unref (toplevel);
return FALSE;
}
emit_notify = res == -1;
} }
gst_object_unref (toplevel); gst_object_unref (toplevel);
@ -1295,7 +1283,7 @@ ges_timeline_element_set_duration (GESTimelineElement * self,
gint res = klass->set_duration (self, duration); gint res = klass->set_duration (self, duration);
if (res == FALSE) if (res == FALSE)
return FALSE; return FALSE;
if (res == TRUE && emit_notify) { if (res == TRUE) {
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]);
} }
@ -2428,9 +2416,15 @@ ges_timeline_element_get_layer_priority (GESTimelineElement * self)
* out of bounds, or if it would place the timeline in an unsupported * out of bounds, or if it would place the timeline in an unsupported
* configuration. * configuration.
* *
* Note that if you act on a #GESTrackElement, this will edit its parent
* #GESClip instead. Moreover, for any #GESTimelineElement, if you select
* #GES_EDGE_NONE for #GES_EDIT_MODE_NORMAL or #GES_EDIT_MODE_RIPPLE, this
* will edit the toplevel instead, but still in such a way as to make the
* #GESTimelineElement:start of @self reach the edit @position.
*
* Note that if the element's timeline has a * Note that if the element's timeline has a
* #GESTimeline:snapping-distance set, then the edit position may be set * #GESTimeline:snapping-distance set, then the edit position may be
* to the edge of some element in the neighbourhood of @position. * snapped to the edge of some element under the edited element.
* *
* @new_layer_priority can be used to switch @self, and other elements * @new_layer_priority can be used to switch @self, and other elements
* moved by the edit, to a new layer. New layers may be be created if the * moved by the edit, to a new layer. New layers may be be created if the
@ -2455,35 +2449,26 @@ ges_timeline_element_edit (GESTimelineElement * self, GList * layers,
gint64 new_layer_priority, GESEditMode mode, GESEdge edge, guint64 position) gint64 new_layer_priority, GESEditMode mode, GESEdge edge, guint64 position)
{ {
GESTimeline *timeline; GESTimeline *timeline;
guint32 layer_prio;
g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE); g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (position), FALSE); g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (position), FALSE);
timeline = GES_TIMELINE_ELEMENT_TIMELINE (self); timeline = GES_TIMELINE_ELEMENT_TIMELINE (self);
/* FIXME: handle a NULL timeline! */ g_return_val_if_fail (timeline, FALSE);
switch (mode) {
case GES_EDIT_MODE_RIPPLE: layer_prio = GES_TIMELINE_ELEMENT_LAYER_PRIORITY (self);
return timeline_ripple_object (timeline, self,
new_layer_priority < if (new_layer_priority < 0)
0 ? GES_TIMELINE_ELEMENT_LAYER_PRIORITY (self) : new_layer_priority = layer_prio;
new_layer_priority, layers, edge, position);
case GES_EDIT_MODE_TRIM: GST_DEBUG_OBJECT (self, "Editing %s at edge %s to position %"
return timeline_trim_object (timeline, self, GST_TIME_FORMAT " under %s mode, and to layer %" G_GINT64_FORMAT,
new_layer_priority < self->name, ges_edge_name (edge), GST_TIME_ARGS (position),
0 ? GES_TIMELINE_ELEMENT_LAYER_PRIORITY (self) : ges_edit_mode_name (mode), new_layer_priority);
new_layer_priority, layers, edge, position);
case GES_EDIT_MODE_NORMAL: return ges_timeline_edit (timeline, self, layers, new_layer_priority, mode,
return timeline_move_object (timeline, self, edge, position);
new_layer_priority <
0 ? GES_TIMELINE_ELEMENT_LAYER_PRIORITY (self) :
new_layer_priority, layers, edge, position);
case GES_EDIT_MODE_ROLL:
return timeline_roll_object (timeline, self, layers, edge, position);
case GES_EDIT_MODE_SLIDE:
GST_ERROR_OBJECT (self, "Sliding not implemented.");
return FALSE;
}
return FALSE;
} }
/** /**

File diff suppressed because it is too large Load diff

View file

@ -13,19 +13,18 @@ gboolean timeline_tree_can_move_element (GNode *root,
GESTimelineElement *element, GESTimelineElement *element,
guint32 priority, guint32 priority,
GstClockTime start, GstClockTime start,
GstClockTime duration, GstClockTime duration);
GList *moving_track_elements);
gboolean timeline_tree_ripple (GNode *root, gboolean timeline_tree_ripple (GNode *root,
GESTimelineElement *element,
gint64 layer_priority_offset, gint64 layer_priority_offset,
GstClockTimeDiff offset, GstClockTimeDiff offset,
GESTimelineElement *rippled_element, GESEdge edge,
GESEdge moving_edge,
GstClockTime snapping_distance); GstClockTime snapping_distance);
void ges_timeline_emit_snapping (GESTimeline * timeline, void ges_timeline_emit_snapping (GESTimeline * timeline,
GESTimelineElement * elem1, GESTrackElement * elem1,
GESTimelineElement * elem2, GESTrackElement * elem2,
GstClockTime snap_time); GstClockTime snap_time);
gboolean timeline_tree_trim (GNode *root, gboolean timeline_tree_trim (GNode *root,

View file

@ -146,10 +146,6 @@ struct _GESTimelinePrivate
GESTrackElement *last_snaped1; GESTrackElement *last_snaped1;
GESTrackElement *last_snaped2; GESTrackElement *last_snaped2;
/* This variable is set to %TRUE when it makes sense to update the transitions,
* and %FALSE otherwize */
gboolean needs_transitions_update;
GESTrack *auto_transition_track; GESTrack *auto_transition_track;
GESTrack *new_track; GESTrack *new_track;
@ -559,9 +555,9 @@ ges_timeline_class_init (GESTimelineClass * klass)
* GESTimeline:snapping-distance: * GESTimeline:snapping-distance:
* *
* The distance (in nanoseconds) at which a #GESTimelineElement being * The distance (in nanoseconds) at which a #GESTimelineElement being
* moved within the timeline should snap to its neighbours. Note that * moved within the timeline should snap one of its #GESSource-s with
* such a neighbour includes any element in the timeline, including * another #GESSource-s edge. See #GESEditMode for which edges can
* across separate layers. 0 means no snapping. * snap during an edit. 0 means no snapping.
*/ */
properties[PROP_SNAPPING_DISTANCE] = properties[PROP_SNAPPING_DISTANCE] =
g_param_spec_uint64 ("snapping-distance", "Snapping distance", g_param_spec_uint64 ("snapping-distance", "Snapping distance",
@ -674,9 +670,14 @@ ges_timeline_class_init (GESTimelineClass * klass)
* @position: The position where the two objects will snap to * @position: The position where the two objects will snap to
* *
* Will be emitted whenever an element's movement invokes a snapping * Will be emitted whenever an element's movement invokes a snapping
* event (usually by its controlling #GESClip being moved) because its * event during an edit (usually of one of its ancestors) because its
* start or end point lies within the #GESTimeline:snapping-distance of * start or end point lies within the #GESTimeline:snapping-distance of
* another element's start or end point. * another element's start or end point.
*
* See #GESEditMode to see what can snap during an edit.
*
* Note that only up to one snapping-started signal will be emitted per
* element edit within a timeline.
*/ */
ges_timeline_signals[SNAPING_STARTED] = ges_timeline_signals[SNAPING_STARTED] =
g_signal_new ("snapping-started", G_TYPE_FROM_CLASS (klass), g_signal_new ("snapping-started", G_TYPE_FROM_CLASS (klass),
@ -692,11 +693,10 @@ ges_timeline_class_init (GESTimelineClass * klass)
* @position: The position where the two objects were to be snapped to * @position: The position where the two objects were to be snapped to
* *
* Will be emitted whenever a snapping event ends. After a snap event * Will be emitted whenever a snapping event ends. After a snap event
* has started (see #GESTimeline::snapping-started), it can end because * has started (see #GESTimeline::snapping-started), it can later end
* the element whose movement created the snap event has since moved * because either another timeline edit has occurred (which may or may
* outside of the #GESTimeline:snapping-distance before its position was * not have created a new snapping event), or because the timeline has
* committed. It can also end because the element's movement was ended * been committed.
* by a timeline being committed.
*/ */
ges_timeline_signals[SNAPING_ENDED] = ges_timeline_signals[SNAPING_ENDED] =
g_signal_new ("snapping-ended", G_TYPE_FROM_CLASS (klass), g_signal_new ("snapping-ended", G_TYPE_FROM_CLASS (klass),
@ -805,7 +805,6 @@ ges_timeline_init (GESTimeline * self)
self->priv->last_snap_ts = GST_CLOCK_TIME_NONE; self->priv->last_snap_ts = GST_CLOCK_TIME_NONE;
priv->priv_tracks = NULL; priv->priv_tracks = NULL;
priv->needs_transitions_update = TRUE;
priv->all_elements = priv->all_elements =
g_hash_table_new_full (g_str_hash, g_str_equal, g_free, gst_object_unref); g_hash_table_new_full (g_str_hash, g_str_equal, g_free, gst_object_unref);
@ -1029,8 +1028,8 @@ _create_auto_transition_from_transitions (GESTimeline * timeline,
} }
void void
ges_timeline_emit_snapping (GESTimeline * timeline, GESTimelineElement * elem1, ges_timeline_emit_snapping (GESTimeline * timeline, GESTrackElement * elem1,
GESTimelineElement * elem2, GstClockTime snap_time) GESTrackElement * elem2, GstClockTime snap_time)
{ {
GESTimelinePrivate *priv = timeline->priv; GESTimelinePrivate *priv = timeline->priv;
GstClockTime last_snap_ts = timeline->priv->last_snap_ts; GstClockTime last_snap_ts = timeline->priv->last_snap_ts;
@ -1048,15 +1047,6 @@ ges_timeline_emit_snapping (GESTimeline * timeline, GESTimelineElement * elem1,
} }
g_assert (elem1 != elem2); g_assert (elem1 != elem2);
if (GES_IS_CLIP (elem1)) {
g_assert (GES_CONTAINER_CHILDREN (elem1));
elem1 = GES_CONTAINER_CHILDREN (elem1)->data;
}
if (GES_IS_CLIP (elem2)) {
g_assert (GES_CONTAINER_CHILDREN (elem2));
elem2 = GES_CONTAINER_CHILDREN (elem2)->data;
}
if (last_snap_ts != snap_time) { if (last_snap_ts != snap_time) {
g_signal_emit (timeline, ges_timeline_signals[SNAPING_ENDED], 0, g_signal_emit (timeline, ges_timeline_signals[SNAPING_ENDED], 0,
@ -1067,10 +1057,9 @@ ges_timeline_emit_snapping (GESTimeline * timeline, GESTimelineElement * elem1,
} }
if (!GST_CLOCK_TIME_IS_VALID (timeline->priv->last_snap_ts)) { if (!GST_CLOCK_TIME_IS_VALID (timeline->priv->last_snap_ts)) {
priv->last_snaped1 = (GESTrackElement *) elem1; priv->last_snaped1 = elem1;
priv->last_snaped2 = (GESTrackElement *) elem2; priv->last_snaped2 = elem2;
timeline->priv->last_snap_ts = snap_time; timeline->priv->last_snap_ts = snap_time;
g_signal_emit (timeline, ges_timeline_signals[SNAPING_STARTED], 0, g_signal_emit (timeline, ges_timeline_signals[SNAPING_STARTED], 0,
elem1, elem2, snap_time); elem1, elem2, snap_time);
} }
@ -1129,91 +1118,14 @@ done:
} }
gboolean static gint
ges_timeline_trim_object_simple (GESTimeline * timeline, _edit_auto_transition (GESTimeline * timeline, GESTimelineElement * element,
GESTimelineElement * element, guint32 new_layer_priority, GList * layers, gint64 new_layer_priority, GESEditMode mode, GESEdge edge,
GList * layers, GESEdge edge, guint64 position, gboolean snapping) GstClockTime position)
{
return timeline_trim_object (timeline, element, new_layer_priority, layers,
edge, position);
}
gboolean
timeline_ripple_object (GESTimeline * timeline, GESTimelineElement * obj,
gint new_layer_priority, GList * layers, GESEdge edge, guint64 position)
{
gboolean res = TRUE;
guint64 new_duration;
GstClockTimeDiff diff;
switch (edge) {
case GES_EDGE_NONE:
GST_DEBUG ("Simply rippling");
diff = GST_CLOCK_DIFF (position, _START (obj));
timeline->priv->needs_transitions_update = FALSE;
res = timeline_tree_ripple (timeline->priv->tree,
(gint64) GES_TIMELINE_ELEMENT_LAYER_PRIORITY (obj) -
(gint64) new_layer_priority, diff, obj,
GES_EDGE_NONE, timeline->priv->snapping_distance);
timeline->priv->needs_transitions_update = TRUE;
break;
case GES_EDGE_END:
GST_DEBUG ("Rippling end");
timeline->priv->needs_transitions_update = FALSE;
new_duration =
CLAMP (GST_CLOCK_DIFF (obj->start, position), 0,
GST_CLOCK_TIME_IS_VALID (obj->
maxduration) ? GST_CLOCK_DIFF (obj->inpoint,
obj->maxduration) : GST_CLOCK_TIME_NONE);
res =
timeline_tree_ripple (timeline->priv->tree,
(gint64) GES_TIMELINE_ELEMENT_LAYER_PRIORITY (obj) -
(gint64) new_layer_priority, _DURATION (obj) - new_duration, obj,
GES_EDGE_END, timeline->priv->snapping_distance);
timeline->priv->needs_transitions_update = TRUE;
GST_DEBUG ("Done Rippling end");
break;
case GES_EDGE_START:
GST_INFO ("Ripple start doesn't make sense, trimming instead");
if (!timeline_trim_object (timeline, obj, -1, layers, edge, position))
goto error;
break;
default:
GST_DEBUG ("Can not ripple edge: %i", edge);
break;
}
return res;
error:
return FALSE;
}
gboolean
timeline_slide_object (GESTimeline * timeline, GESTrackElement * obj,
GList * layers, GESEdge edge, guint64 position)
{
/* FIXME implement me! */
GST_FIXME_OBJECT (timeline, "Slide mode editing not implemented yet");
return FALSE;
}
static gboolean
_trim_transition (GESTimeline * timeline, GESTimelineElement * element,
GESEdge edge, GstClockTime position)
{ {
GList *tmp; GList *tmp;
GESLayer *layer = ges_timeline_get_layer (timeline, guint32 layer_prio = ges_timeline_element_get_layer_priority (element);
GES_TIMELINE_ELEMENT_LAYER_PRIORITY (element)); GESLayer *layer = ges_timeline_get_layer (timeline, layer_prio);
if (!ges_layer_get_auto_transition (layer)) { if (!ges_layer_get_auto_transition (layer)) {
gst_object_unref (layer); gst_object_unref (layer);
@ -1222,86 +1134,83 @@ _trim_transition (GESTimeline * timeline, GESTimelineElement * element,
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) {
GESTimelineElement *replace;
GESAutoTransition *auto_transition = tmp->data; GESAutoTransition *auto_transition = tmp->data;
if (GES_TIMELINE_ELEMENT (auto_transition->transition) == element || if (GES_TIMELINE_ELEMENT (auto_transition->transition) == element ||
GES_TIMELINE_ELEMENT (auto_transition->transition_clip) == element) { GES_TIMELINE_ELEMENT (auto_transition->transition_clip) == element) {
/* Trimming an auto transition means trimming its neighboors */ if (auto_transition->positioning) {
if (!auto_transition->positioning) { GST_ERROR_OBJECT (element, "Trying to edit an auto-transition "
if (edge == GES_EDGE_END) { "whilst it is being positioned");
ges_container_edit (GES_CONTAINER (auto_transition->previous_clip), return FALSE;
NULL, -1, GES_EDIT_MODE_TRIM, GES_EDGE_END, position); }
} else { if (new_layer_priority != layer_prio) {
ges_container_edit (GES_CONTAINER (auto_transition->next_clip), GST_WARNING_OBJECT (element, "Cannot edit an auto-transition to a "
NULL, -1, GES_EDIT_MODE_TRIM, GES_EDGE_START, position); "new layer");
} return FALSE;
}
return TRUE; if (mode != GES_EDIT_MODE_TRIM) {
GST_WARNING_OBJECT (element, "Cannot edit an auto-transition "
"under the edit mode %i", mode);
return FALSE;
} }
return FALSE; if (edge == GES_EDGE_END)
replace = GES_TIMELINE_ELEMENT (auto_transition->previous_clip);
else
replace = GES_TIMELINE_ELEMENT (auto_transition->next_clip);
GST_INFO_OBJECT (element, "Trimming clip %" GES_FORMAT " in place "
"of trimming the corresponding auto-transition", GES_ARGS (replace));
return ges_timeline_element_edit (replace, layers, -1, mode, edge,
position);
} }
} }
return FALSE; return -1;
} }
gboolean gboolean
timeline_trim_object (GESTimeline * timeline, GESTimelineElement * object, ges_timeline_edit (GESTimeline * timeline, GESTimelineElement * element,
guint32 new_layer_priority, GList * layers, GESEdge edge, guint64 position) GList * layers, gint64 new_layer_priority, GESEditMode mode, GESEdge edge,
{
if ((GES_IS_TRANSITION (object) || GES_IS_TRANSITION_CLIP (object)) &&
!ELEMENT_FLAG_IS_SET (object, GES_TIMELINE_ELEMENT_SET_SIMPLE)) {
return _trim_transition (timeline, object, edge, position);
}
return timeline_tree_trim (timeline->priv->tree,
GES_TIMELINE_ELEMENT (object), (gint64)
ges_timeline_element_get_layer_priority (GES_TIMELINE_ELEMENT (object)) -
(gint64) new_layer_priority,
edge == GES_EDGE_END ? GST_CLOCK_DIFF (position,
_START (object) + _DURATION (object)) : GST_CLOCK_DIFF (position,
GES_TIMELINE_ELEMENT_START (object)), edge,
timeline->priv->snapping_distance);
}
gboolean
timeline_roll_object (GESTimeline * timeline, GESTimelineElement * element,
GList * layers, GESEdge edge, guint64 position)
{
return timeline_tree_roll (timeline->priv->tree,
element,
(edge == GES_EDGE_END) ?
GST_CLOCK_DIFF (position, _END (element)) :
GST_CLOCK_DIFF (position, _START (element)),
edge, timeline->priv->snapping_distance);
}
gboolean
timeline_move_object (GESTimeline * timeline, GESTimelineElement * object,
guint32 new_layer_priority, GList * layers, GESEdge edge, guint64 position)
{
gboolean ret = FALSE;
GstClockTimeDiff offset = edge == GES_EDGE_END ?
GST_CLOCK_DIFF (position, _START (object) + _DURATION (object)) :
GST_CLOCK_DIFF (position, GES_TIMELINE_ELEMENT_START (object));
ret = timeline_tree_move (timeline->priv->tree,
GES_TIMELINE_ELEMENT (object), (gint64)
ges_timeline_element_get_layer_priority (GES_TIMELINE_ELEMENT (object)) -
(gint64) new_layer_priority, offset, edge,
timeline->priv->snapping_distance);
return ret;
}
gboolean
ges_timeline_move_object_simple (GESTimeline * timeline,
GESTimelineElement * element, GList * layers, GESEdge edge,
guint64 position) guint64 position)
{ {
return timeline_move_object (timeline, element, GstClockTimeDiff edge_diff = (edge == GES_EDGE_END ?
ges_timeline_element_get_layer_priority (element), NULL, edge, position); GST_CLOCK_DIFF (position, element->start + element->duration) :
GST_CLOCK_DIFF (position, element->start));
gint64 prio_diff = (gint64) ges_timeline_element_get_layer_priority (element)
- new_layer_priority;
gint res = -1;
if ((GES_IS_TRANSITION (element) || GES_IS_TRANSITION_CLIP (element)))
res = _edit_auto_transition (timeline, element, layers, new_layer_priority,
mode, edge, position);
if (res != -1)
return res;
switch (mode) {
case GES_EDIT_MODE_RIPPLE:
return timeline_tree_ripple (timeline->priv->tree, element, prio_diff,
edge_diff, edge, timeline->priv->snapping_distance);
case GES_EDIT_MODE_TRIM:
return timeline_tree_trim (timeline->priv->tree, element, prio_diff,
edge_diff, edge, timeline->priv->snapping_distance);
case GES_EDIT_MODE_NORMAL:
return timeline_tree_move (timeline->priv->tree, element, prio_diff,
edge_diff, edge, timeline->priv->snapping_distance);
case GES_EDIT_MODE_ROLL:
if (prio_diff != 0) {
GST_WARNING_OBJECT (element, "Cannot roll an element to a new layer");
return FALSE;
}
return timeline_tree_roll (timeline->priv->tree, element,
edge_diff, edge, timeline->priv->snapping_distance);
case GES_EDIT_MODE_SLIDE:
GST_ERROR_OBJECT (element, "Sliding not implemented.");
return FALSE;
}
return FALSE;
} }
void void

View file

@ -1199,10 +1199,11 @@ ges_track_add_element (GESTrack * track, GESTrackElement * object)
timeline = track->priv->timeline; timeline = track->priv->timeline;
ges_timeline_element_set_timeline (el, timeline); ges_timeline_element_set_timeline (el, timeline);
/* check that we haven't broken the timeline configuration by adding this
* element to the track */
if (timeline if (timeline
&& !timeline_tree_can_move_element (timeline_get_tree (timeline), el, && !timeline_tree_can_move_element (timeline_get_tree (timeline), el,
GES_TIMELINE_ELEMENT_LAYER_PRIORITY (el), el->start, el->duration, GES_TIMELINE_ELEMENT_LAYER_PRIORITY (el), el->start, el->duration)) {
NULL)) {
GST_WARNING_OBJECT (track, GST_WARNING_OBJECT (track,
"Could not add the track element %" GES_FORMAT "Could not add the track element %" GES_FORMAT
" to the track because it breaks the timeline " "configuration rules", " to the track because it breaks the timeline " "configuration rules",

View file

@ -395,8 +395,7 @@ GST_START_TEST (test_single_layer_automatic_transition)
transition = objects->next->next->data; transition = objects->next->next->data;
assert_is_type (transition, GES_TYPE_TRANSITION_CLIP); assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
assert_equals_int (_START (transition), 500); CHECK_OBJECT_PROPS (transition, 500, 0, 750);
assert_equals_uint64 (_DURATION (transition), 750);
g_list_free_full (objects, gst_object_unref); g_list_free_full (objects, gst_object_unref);
fail_if (ges_timeline_element_set_start (src1, 250)); fail_if (ges_timeline_element_set_start (src1, 250));

View file

@ -376,8 +376,6 @@ GST_START_TEST (test_snapping)
*/ */
fail_unless (ges_timeline_element_set_inpoint (GES_TIMELINE_ELEMENT (clip2), fail_unless (ges_timeline_element_set_inpoint (GES_TIMELINE_ELEMENT (clip2),
5)); 5));
fail_unless (ges_timeline_element_roll_start (GES_TIMELINE_ELEMENT (clip2),
60));
DEEP_CHECK (clip, 30, 5, 32); DEEP_CHECK (clip, 30, 5, 32);
DEEP_CHECK (clip1, 20, 0, 10); DEEP_CHECK (clip1, 20, 0, 10);
DEEP_CHECK (clip2, 62, 5, 60); DEEP_CHECK (clip2, 62, 5, 60);
@ -912,9 +910,6 @@ GST_START_TEST (test_timeline_edition_mode)
CHECK_OBJECT_PROPS (trackelement1, 25, 5, 47); CHECK_OBJECT_PROPS (trackelement1, 25, 5, 47);
CHECK_OBJECT_PROPS (trackelement2, 72, 10, 50); CHECK_OBJECT_PROPS (trackelement2, 72, 10, 50);
fail_if (ges_container_edit (clip2, NULL, -1, GES_EDIT_MODE_ROLL,
GES_EDGE_START, 59));
ges_deinit (); ges_deinit ();
} }
@ -1010,8 +1005,6 @@ GST_START_TEST (test_groups)
fail_if (ges_container_edit (GES_CONTAINER (c1), NULL, 2, fail_if (ges_container_edit (GES_CONTAINER (c1), NULL, 2,
GES_EDIT_MODE_RIPPLE, GES_EDGE_END, 40) == TRUE); GES_EDIT_MODE_RIPPLE, GES_EDGE_END, 40) == TRUE);
fail_if (ges_container_edit (GES_CONTAINER (c1), NULL, 2,
GES_EDIT_MODE_RIPPLE, GES_EDGE_END, 30) == TRUE);
CHECK_CLIP (c, 10, 0, 10, 1); CHECK_CLIP (c, 10, 0, 10, 1);
CHECK_CLIP (c1, 20, 0, 10, 2); CHECK_CLIP (c1, 20, 0, 10, 2);
CHECK_CLIP (c2, 30, 0, 10, 2); CHECK_CLIP (c2, 30, 0, 10, 2);

View file

@ -504,14 +504,14 @@ class TestEditing(common.GESSimpleTimelineTest):
def test_trim_start(self): def test_trim_start(self):
clip = self.append_clip() clip = self.append_clip()
self.assertTrue(clip.edit([], -1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 10)) self.assertTrue(clip.edit([], -1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_START, 10))
self.assertTimelineTopology([ self.assertTimelineTopology([
[ # Unique layer [ # Unique layer
(GES.TestClip, 10, 10), (GES.TestClip, 10, 10),
] ]
]) ])
self.assertFalse(clip.edit([], -1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_NONE, 0)) self.assertFalse(clip.edit([], -1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 0))
self.assertTimelineTopology([ self.assertTimelineTopology([
[ # Unique layer [ # Unique layer
(GES.TestClip, 10, 10), (GES.TestClip, 10, 10),

View file

@ -13,11 +13,11 @@ check-last-sample, sinkpad-caps="video/x-raw", timecode-frame-number=100
edit, element-name=clip, edit-mode=normal, position=1.0 edit, element-name=clip, edit-mode=normal, position=1.0
edit, element-name=clip, edit-mode=edit_trim, source-frame=60 edit, element-name=clip, edit-mode=edit_trim, edge=start, source-frame=60
edit, element-name=clip, position=0 edit, element-name=clip, position=0
commit; commit;
check-last-sample, sinkpad-caps="video/x-raw", timecode-frame-number=60 check-last-sample, sinkpad-caps="video/x-raw", timecode-frame-number=60
edit, element-name=clip, edit-mode=edit_trim, edit-edge=end, source-frame=120 edit, element-name=clip, edit-mode=edit_trim, edge=start, source-frame=120
check-ges-properties, element-name=clip, start=0.5 check-ges-properties, element-name=clip, start=0.5
stop stop