mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 11:41:09 +00:00
errors: added edit errors
Added more errors to GES_ERROR for when edits fail (other than programming or usage errors). Also promoted some GST messages if they related to a usage error. Also added explanation of timeline overlap rules in user docs. Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-editing-services/-/merge_requests/177>
This commit is contained in:
parent
a507f7017e
commit
142456d8ba
19 changed files with 1151 additions and 479 deletions
214
ges/ges-clip.c
214
ges/ges-clip.c
|
@ -212,10 +212,6 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GESClip, ges_clip,
|
|||
(GST_CLOCK_TIME_IS_VALID (a) ? \
|
||||
(GST_CLOCK_TIME_IS_VALID (b) ? MIN (a, b) : a) : b) \
|
||||
|
||||
#define _CLOCK_TIME_IS_LESS(first, second) \
|
||||
(GST_CLOCK_TIME_IS_VALID (first) && (!GST_CLOCK_TIME_IS_VALID (second) \
|
||||
|| first < second))
|
||||
|
||||
/****************************************************
|
||||
* duration-limit *
|
||||
****************************************************/
|
||||
|
@ -366,7 +362,7 @@ _update_duration_limit (GESClip * self)
|
|||
GST_INFO_OBJECT (self, "duration-limit for the clip is %"
|
||||
GST_TIME_FORMAT, GST_TIME_ARGS (duration_limit));
|
||||
|
||||
if (_CLOCK_TIME_IS_LESS (duration_limit, element->duration)
|
||||
if (GES_CLOCK_TIME_IS_LESS (duration_limit, element->duration)
|
||||
&& !GES_TIMELINE_ELEMENT_BEING_EDITED (self)) {
|
||||
gboolean res;
|
||||
|
||||
|
@ -379,7 +375,7 @@ _update_duration_limit (GESClip * self)
|
|||
if (element->timeline)
|
||||
res = timeline_tree_trim (timeline_get_tree (element->timeline),
|
||||
element, 0, GST_CLOCK_DIFF (duration_limit, element->duration),
|
||||
GES_EDGE_END, 0);
|
||||
GES_EDGE_END, 0, NULL);
|
||||
else
|
||||
res = ges_timeline_element_set_duration (element, duration_limit);
|
||||
|
||||
|
@ -396,18 +392,18 @@ _update_duration_limit (GESClip * self)
|
|||
|
||||
/* transfer full of child_data */
|
||||
static gboolean
|
||||
_can_update_duration_limit (GESClip * self, GList * child_data)
|
||||
_can_update_duration_limit (GESClip * self, GList * child_data, GError ** error)
|
||||
{
|
||||
GESTimeline *timeline = GES_TIMELINE_ELEMENT_TIMELINE (self);
|
||||
GstClockTime duration = _calculate_duration_limit (self, child_data);
|
||||
GESTimelineElement *element = GES_TIMELINE_ELEMENT (self);
|
||||
|
||||
if (_CLOCK_TIME_IS_LESS (duration, element->duration)) {
|
||||
if (GES_CLOCK_TIME_IS_LESS (duration, element->duration)) {
|
||||
/* NOTE: timeline would normally not be NULL at this point */
|
||||
if (timeline
|
||||
&& !timeline_tree_can_move_element (timeline_get_tree (timeline),
|
||||
element, ges_timeline_element_get_layer_priority (element),
|
||||
element->start, duration)) {
|
||||
element->start, duration, error)) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
@ -446,7 +442,7 @@ _get_priority_range (GESContainer * container, guint32 * min_priority,
|
|||
|
||||
gboolean
|
||||
ges_clip_can_set_priority_of_child (GESClip * clip, GESTrackElement * child,
|
||||
guint32 priority)
|
||||
guint32 priority, GError ** error)
|
||||
{
|
||||
GList *child_data;
|
||||
DurationLimitData *data;
|
||||
|
@ -459,7 +455,7 @@ ges_clip_can_set_priority_of_child (GESClip * clip, GESTrackElement * child,
|
|||
|
||||
child_data = _duration_limit_data_list_with_data (clip, data);
|
||||
|
||||
if (!_can_update_duration_limit (clip, child_data)) {
|
||||
if (!_can_update_duration_limit (clip, child_data, error)) {
|
||||
GST_INFO_OBJECT (clip, "Cannot move the child %" GES_FORMAT " from "
|
||||
"priority %" G_GUINT32_FORMAT " to %" G_GUINT32_FORMAT " because "
|
||||
"the duration-limit cannot be adjusted", GES_ARGS (child),
|
||||
|
@ -489,7 +485,8 @@ _child_priority_changed (GESContainer * container, GESTimelineElement * child)
|
|||
****************************************************/
|
||||
|
||||
static gboolean
|
||||
_can_set_inpoint_of_core_children (GESClip * clip, GstClockTime inpoint)
|
||||
_can_set_inpoint_of_core_children (GESClip * clip, GstClockTime inpoint,
|
||||
GError ** error)
|
||||
{
|
||||
GList *tmp;
|
||||
GList *child_data = NULL;
|
||||
|
@ -505,13 +502,18 @@ _can_set_inpoint_of_core_children (GESClip * clip, GstClockTime inpoint)
|
|||
_duration_limit_data_new (GES_TRACK_ELEMENT (child));
|
||||
|
||||
if (_IS_CORE_INTERNAL_SOURCE_CHILD (child)) {
|
||||
if (GST_CLOCK_TIME_IS_VALID (child->maxduration)
|
||||
&& child->maxduration < inpoint) {
|
||||
if (GES_CLOCK_TIME_IS_LESS (child->maxduration, inpoint)) {
|
||||
GST_INFO_OBJECT (clip, "Cannot set the in-point from %"
|
||||
G_GUINT64_FORMAT " to %" G_GUINT64_FORMAT " because it would "
|
||||
GST_TIME_FORMAT " to %" GST_TIME_FORMAT " because it would "
|
||||
"cause the in-point of its core child %" GES_FORMAT
|
||||
" to exceed its max-duration", _INPOINT (clip), inpoint,
|
||||
GES_ARGS (child));
|
||||
" to exceed its max-duration", GST_TIME_ARGS (_INPOINT (clip)),
|
||||
GST_TIME_ARGS (inpoint), GES_ARGS (child));
|
||||
g_set_error (error, GES_ERROR, GES_ERROR_NOT_ENOUGH_INTERNAL_CONTENT,
|
||||
"Cannot set the in-point of \"%s\" to %" GST_TIME_FORMAT
|
||||
" because it would exceed the max-duration of %" GST_TIME_FORMAT
|
||||
" for the child \"%s\"", GES_TIMELINE_ELEMENT_NAME (clip),
|
||||
GST_TIME_ARGS (inpoint), GST_TIME_ARGS (child->maxduration),
|
||||
child->name);
|
||||
|
||||
_duration_limit_data_free (data);
|
||||
g_list_free_full (child_data, _duration_limit_data_free);
|
||||
|
@ -524,10 +526,10 @@ _can_set_inpoint_of_core_children (GESClip * clip, GstClockTime inpoint)
|
|||
child_data = g_list_prepend (child_data, data);
|
||||
}
|
||||
|
||||
if (!_can_update_duration_limit (clip, child_data)) {
|
||||
GST_INFO_OBJECT (clip, "Cannot set the in-point from %" G_GUINT64_FORMAT
|
||||
" to %" G_GUINT64_FORMAT " because the duration-limit cannot be "
|
||||
"adjusted", _INPOINT (clip), inpoint);
|
||||
if (!_can_update_duration_limit (clip, child_data, error)) {
|
||||
GST_INFO_OBJECT (clip, "Cannot set the in-point from %" GST_TIME_FORMAT
|
||||
" to %" GST_TIME_FORMAT " because the duration-limit cannot be "
|
||||
"adjusted", GST_TIME_ARGS (_INPOINT (clip)), GST_TIME_ARGS (inpoint));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
@ -538,7 +540,7 @@ _can_set_inpoint_of_core_children (GESClip * clip, GstClockTime inpoint)
|
|||
* its children have a max-duration below it */
|
||||
gboolean
|
||||
ges_clip_can_set_inpoint_of_child (GESClip * clip, GESTrackElement * child,
|
||||
GstClockTime inpoint)
|
||||
GstClockTime inpoint, GError ** error)
|
||||
{
|
||||
/* don't bother checking if we are setting the value */
|
||||
if (clip->priv->setting_inpoint)
|
||||
|
@ -555,11 +557,11 @@ ges_clip_can_set_inpoint_of_child (GESClip * clip, GESTrackElement * child,
|
|||
|
||||
child_data = _duration_limit_data_list_with_data (clip, data);
|
||||
|
||||
if (!_can_update_duration_limit (clip, child_data)) {
|
||||
if (!_can_update_duration_limit (clip, child_data, error)) {
|
||||
GST_INFO_OBJECT (clip, "Cannot set the in-point of non-core child %"
|
||||
GES_FORMAT " from %" G_GUINT64_FORMAT " to %" G_GUINT64_FORMAT
|
||||
GES_FORMAT " from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT
|
||||
" because the duration-limit cannot be adjusted", GES_ARGS (child),
|
||||
_INPOINT (child), inpoint);
|
||||
GST_TIME_ARGS (_INPOINT (child)), GST_TIME_ARGS (inpoint));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
@ -568,7 +570,7 @@ ges_clip_can_set_inpoint_of_child (GESClip * clip, GESTrackElement * child,
|
|||
|
||||
/* setting the in-point of a core child will shift the in-point of all
|
||||
* core children with an internal source */
|
||||
return _can_set_inpoint_of_core_children (clip, inpoint);
|
||||
return _can_set_inpoint_of_core_children (clip, inpoint, error);
|
||||
}
|
||||
|
||||
/* returns TRUE if duration-limit needs to be updated */
|
||||
|
@ -622,7 +624,7 @@ _update_max_duration (GESContainer * container)
|
|||
|
||||
gboolean
|
||||
ges_clip_can_set_max_duration_of_child (GESClip * clip, GESTrackElement * child,
|
||||
GstClockTime max_duration)
|
||||
GstClockTime max_duration, GError ** error)
|
||||
{
|
||||
GList *child_data;
|
||||
DurationLimitData *data;
|
||||
|
@ -635,11 +637,11 @@ ges_clip_can_set_max_duration_of_child (GESClip * clip, GESTrackElement * child,
|
|||
|
||||
child_data = _duration_limit_data_list_with_data (clip, data);
|
||||
|
||||
if (!_can_update_duration_limit (clip, child_data)) {
|
||||
if (!_can_update_duration_limit (clip, child_data, error)) {
|
||||
GST_INFO_OBJECT (clip, "Cannot set the max-duration of child %"
|
||||
GES_FORMAT " from %" G_GUINT64_FORMAT " to %" G_GUINT64_FORMAT
|
||||
GES_FORMAT " from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT
|
||||
" because the duration-limit cannot be adjusted", GES_ARGS (child),
|
||||
_MAXDURATION (child), max_duration);
|
||||
GST_TIME_ARGS (_MAXDURATION (child)), GST_TIME_ARGS (max_duration));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
@ -685,7 +687,7 @@ _child_has_internal_source_changed (GESClip * self, GESTimelineElement * child)
|
|||
|
||||
gboolean
|
||||
ges_clip_can_set_active_of_child (GESClip * clip, GESTrackElement * child,
|
||||
gboolean active)
|
||||
gboolean active, GError ** error)
|
||||
{
|
||||
GESTrack *track = ges_track_element_get_track (child);
|
||||
gboolean is_core = _IS_CORE_CHILD (child);
|
||||
|
@ -722,7 +724,7 @@ ges_clip_can_set_active_of_child (GESClip * clip, GESTrackElement * child,
|
|||
}
|
||||
}
|
||||
|
||||
if (!_can_update_duration_limit (clip, child_data)) {
|
||||
if (!_can_update_duration_limit (clip, child_data, error)) {
|
||||
GST_INFO_OBJECT (clip, "Cannot set the active of child %" GES_FORMAT
|
||||
" from %i to %i because the duration-limit cannot be adjusted",
|
||||
GES_ARGS (child), ges_track_element_is_active (child), active);
|
||||
|
@ -803,7 +805,7 @@ _track_contains_non_core (GESClip * clip, GESTrack * track)
|
|||
|
||||
gboolean
|
||||
ges_clip_can_set_track_of_child (GESClip * clip, GESTrackElement * child,
|
||||
GESTrack * track)
|
||||
GESTrack * track, GError ** error)
|
||||
{
|
||||
GList *child_data;
|
||||
DurationLimitData *data;
|
||||
|
@ -816,12 +818,14 @@ ges_clip_can_set_track_of_child (GESClip * clip, GESTrackElement * child,
|
|||
if (current_track == track)
|
||||
return TRUE;
|
||||
|
||||
/* NOTE: we consider the following error cases programming errors by
|
||||
* the user */
|
||||
if (current_track) {
|
||||
/* can not remove a core element from a track if a non-core one sits
|
||||
* above it */
|
||||
if (_IS_CORE_CHILD (child)
|
||||
&& _track_contains_non_core (clip, current_track)) {
|
||||
GST_INFO_OBJECT (clip, "Cannot move the core child %" GES_FORMAT
|
||||
GST_WARNING_OBJECT (clip, "Cannot move the core child %" GES_FORMAT
|
||||
" to the track %" GST_PTR_FORMAT " because it has non-core "
|
||||
"siblings above it in its current track %" GST_PTR_FORMAT,
|
||||
GES_ARGS (child), track, current_track);
|
||||
|
@ -833,13 +837,13 @@ ges_clip_can_set_track_of_child (GESClip * clip, GESTrackElement * child,
|
|||
GESTimeline *clip_timeline = GES_TIMELINE_ELEMENT_TIMELINE (clip);
|
||||
const GESTimeline *track_timeline = ges_track_get_timeline (track);
|
||||
if (track_timeline == NULL) {
|
||||
GST_INFO_OBJECT (clip, "Cannot move the child %" GES_FORMAT
|
||||
GST_WARNING_OBJECT (clip, "Cannot move the child %" GES_FORMAT
|
||||
" to the track %" GST_PTR_FORMAT " because it is not part "
|
||||
"of a timeline", GES_ARGS (child), track);
|
||||
return FALSE;
|
||||
}
|
||||
if (track_timeline != clip_timeline) {
|
||||
GST_INFO_OBJECT (clip, "Cannot move the child %" GES_FORMAT
|
||||
GST_WARNING_OBJECT (clip, "Cannot move the child %" GES_FORMAT
|
||||
" to the track %" GST_PTR_FORMAT " because its timeline %"
|
||||
GST_PTR_FORMAT " does not match the clip's timeline %"
|
||||
GST_PTR_FORMAT, GES_ARGS (child), track, track_timeline,
|
||||
|
@ -852,7 +856,7 @@ ges_clip_can_set_track_of_child (GESClip * clip, GESTrackElement * child,
|
|||
* placed in a track that already has a core child */
|
||||
if (_IS_CORE_CHILD (child)) {
|
||||
if (core) {
|
||||
GST_INFO_OBJECT (clip, "Cannot move the core child %" GES_FORMAT
|
||||
GST_WARNING_OBJECT (clip, "Cannot move the core child %" GES_FORMAT
|
||||
" to the track %" GST_PTR_FORMAT " because it contains a "
|
||||
"core sibling %" GES_FORMAT, GES_ARGS (child), track,
|
||||
GES_ARGS (core));
|
||||
|
@ -860,7 +864,7 @@ ges_clip_can_set_track_of_child (GESClip * clip, GESTrackElement * child,
|
|||
}
|
||||
} else {
|
||||
if (!core) {
|
||||
GST_INFO_OBJECT (clip, "Cannot move the non-core child %"
|
||||
GST_WARNING_OBJECT (clip, "Cannot move the non-core child %"
|
||||
GES_FORMAT " to the track %" GST_PTR_FORMAT " because it "
|
||||
" does not contain a core sibling", GES_ARGS (child), track);
|
||||
return FALSE;
|
||||
|
@ -881,7 +885,7 @@ ges_clip_can_set_track_of_child (GESClip * clip, GESTrackElement * child,
|
|||
|
||||
child_data = _duration_limit_data_list_with_data (clip, data);
|
||||
|
||||
if (!_can_update_duration_limit (clip, child_data)) {
|
||||
if (!_can_update_duration_limit (clip, child_data, error)) {
|
||||
GST_INFO_OBJECT (clip, "Cannot move the child %" GES_FORMAT " from "
|
||||
"track %" GST_PTR_FORMAT " to track %" GST_PTR_FORMAT " because "
|
||||
"the duration-limit cannot be adjusted", GES_ARGS (child),
|
||||
|
@ -1034,7 +1038,7 @@ _set_childrens_inpoint (GESTimelineElement * element, GstClockTime inpoint,
|
|||
static gboolean
|
||||
_set_inpoint (GESTimelineElement * element, GstClockTime inpoint)
|
||||
{
|
||||
if (!_can_set_inpoint_of_core_children (GES_CLIP (element), inpoint)) {
|
||||
if (!_can_set_inpoint_of_core_children (GES_CLIP (element), inpoint, NULL)) {
|
||||
GST_WARNING_OBJECT (element, "Cannot set the in-point to %"
|
||||
GST_TIME_FORMAT, GST_TIME_ARGS (inpoint));
|
||||
return FALSE;
|
||||
|
@ -1097,10 +1101,11 @@ _set_max_duration (GESTimelineElement * element, GstClockTime maxduration)
|
|||
child_data = g_list_prepend (child_data, data);
|
||||
}
|
||||
|
||||
if (!_can_update_duration_limit (self, child_data)) {
|
||||
if (!_can_update_duration_limit (self, child_data, NULL)) {
|
||||
GST_WARNING_OBJECT (self, "Cannot set the max-duration from %"
|
||||
G_GUINT64_FORMAT " to %" G_GUINT64_FORMAT " because the "
|
||||
"duration-limit cannot be adjusted", element->maxduration, maxduration);
|
||||
GST_TIME_FORMAT " to %" GST_TIME_FORMAT " because the "
|
||||
"duration-limit cannot be adjusted",
|
||||
GST_TIME_ARGS (element->maxduration), GST_TIME_ARGS (maxduration));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
@ -1357,7 +1362,7 @@ _add_child (GESContainer * container, GESTimelineElement * element)
|
|||
|
||||
child_data = _duration_limit_data_list_with_data (self, data);
|
||||
|
||||
if (!_can_update_duration_limit (self, child_data)) {
|
||||
if (!_can_update_duration_limit (self, child_data, NULL)) {
|
||||
GST_WARNING_OBJECT (self, "Cannot add core %" GES_FORMAT " as a "
|
||||
"child because the duration-limit cannot be adjusted",
|
||||
GES_ARGS (element));
|
||||
|
@ -1418,7 +1423,7 @@ _add_child (GESContainer * container, GESTimelineElement * element)
|
|||
data->priority++;
|
||||
}
|
||||
|
||||
if (!_can_update_duration_limit (self, child_data)) {
|
||||
if (!_can_update_duration_limit (self, child_data, NULL)) {
|
||||
GST_WARNING_OBJECT (self, "Cannot add effect %" GES_FORMAT " as "
|
||||
"a child because the duration-limit cannot be adjusted",
|
||||
GES_ARGS (element));
|
||||
|
@ -1502,7 +1507,7 @@ _remove_child (GESContainer * container, GESTimelineElement * element)
|
|||
g_list_prepend (child_data, _duration_limit_data_new (child));
|
||||
}
|
||||
|
||||
if (!_can_update_duration_limit (self, child_data)) {
|
||||
if (!_can_update_duration_limit (self, child_data, NULL)) {
|
||||
GST_WARNING_OBJECT (self, "Cannot remove the child %" GES_FORMAT
|
||||
" because the duration-limit cannot be adjusted", GES_ARGS (el));
|
||||
return FALSE;
|
||||
|
@ -2338,9 +2343,10 @@ ges_clip_is_moving_from_layer (GESClip * clip)
|
|||
}
|
||||
|
||||
/**
|
||||
* ges_clip_move_to_layer:
|
||||
* ges_clip_move_to_layer_full:
|
||||
* @clip: A #GESClip
|
||||
* @layer: The new layer
|
||||
* @error: (nullable): Return location for an error
|
||||
*
|
||||
* Moves a clip to a new layer. If the clip already exists in a layer, it
|
||||
* is first removed from its current layer before being added to the new
|
||||
|
@ -2349,7 +2355,7 @@ ges_clip_is_moving_from_layer (GESClip * clip)
|
|||
* Returns: %TRUE if @clip was successfully moved to @layer.
|
||||
*/
|
||||
gboolean
|
||||
ges_clip_move_to_layer (GESClip * clip, GESLayer * layer)
|
||||
ges_clip_move_to_layer_full (GESClip * clip, GESLayer * layer, GError ** error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
GESLayer *current_layer;
|
||||
|
@ -2357,6 +2363,7 @@ ges_clip_move_to_layer (GESClip * clip, GESLayer * layer)
|
|||
|
||||
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 (!error || !*error, FALSE);
|
||||
|
||||
element = GES_TIMELINE_ELEMENT (clip);
|
||||
current_layer = clip->priv->layer;
|
||||
|
@ -2387,7 +2394,7 @@ ges_clip_move_to_layer (GESClip * clip, GESLayer * layer)
|
|||
/* move to new layer, also checks moving of toplevel */
|
||||
return timeline_tree_move (timeline_get_tree (layer->timeline),
|
||||
element, (gint64) ges_layer_get_priority (current_layer) -
|
||||
(gint64) ges_layer_get_priority (layer), 0, GES_EDGE_NONE, 0);
|
||||
(gint64) ges_layer_get_priority (layer), 0, GES_EDGE_NONE, 0, error);
|
||||
}
|
||||
|
||||
gst_object_ref (clip);
|
||||
|
@ -2418,6 +2425,21 @@ done:
|
|||
return ret && (clip->priv->layer == layer);
|
||||
}
|
||||
|
||||
/**
|
||||
* ges_clip_move_to_layer:
|
||||
* @clip: A #GESClip
|
||||
* @layer: The new layer
|
||||
*
|
||||
* See ges_clip_move_to_layer_full(), which also gives an error.
|
||||
*
|
||||
* Returns: %TRUE if @clip was successfully moved to @layer.
|
||||
*/
|
||||
gboolean
|
||||
ges_clip_move_to_layer (GESClip * clip, GESLayer * layer)
|
||||
{
|
||||
return ges_clip_move_to_layer_full (clip, layer, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* ges_clip_find_track_element:
|
||||
* @clip: A #GESClip
|
||||
|
@ -2603,10 +2625,11 @@ ges_clip_set_top_effect_priority (GESClip * clip,
|
|||
}
|
||||
|
||||
/**
|
||||
* ges_clip_set_top_effect_index:
|
||||
* ges_clip_set_top_effect_index_full:
|
||||
* @clip: A #GESClip
|
||||
* @effect: An effect within @clip to move
|
||||
* @newindex: The index for @effect in @clip
|
||||
* @error: (nullable): Return location for an error
|
||||
*
|
||||
* Set the index of an effect within the clip. See
|
||||
* ges_clip_get_top_effect_index(). The new index must be an existing
|
||||
|
@ -2617,8 +2640,8 @@ ges_clip_set_top_effect_priority (GESClip * clip,
|
|||
* Returns: %TRUE if @effect was successfully moved to @newindex.
|
||||
*/
|
||||
gboolean
|
||||
ges_clip_set_top_effect_index (GESClip * clip, GESBaseEffect * effect,
|
||||
guint newindex)
|
||||
ges_clip_set_top_effect_index_full (GESClip * clip, GESBaseEffect * effect,
|
||||
guint newindex, GError ** error)
|
||||
{
|
||||
gint inc;
|
||||
GList *top_effects, *tmp;
|
||||
|
@ -2628,6 +2651,7 @@ ges_clip_set_top_effect_index (GESClip * clip, GESBaseEffect * effect,
|
|||
|
||||
g_return_val_if_fail (GES_IS_CLIP (clip), FALSE);
|
||||
g_return_val_if_fail (GES_IS_BASE_EFFECT (effect), FALSE);
|
||||
g_return_val_if_fail (!error || !*error, FALSE);
|
||||
|
||||
if (!_is_added_effect (clip, effect))
|
||||
return FALSE;
|
||||
|
@ -2670,8 +2694,8 @@ ges_clip_set_top_effect_index (GESClip * clip, GESBaseEffect * effect,
|
|||
child_data = g_list_prepend (child_data, data);
|
||||
}
|
||||
|
||||
if (!_can_update_duration_limit (clip, child_data)) {
|
||||
GST_WARNING_OBJECT (clip, "Cannot move top effect %" GES_FORMAT
|
||||
if (!_can_update_duration_limit (clip, child_data, error)) {
|
||||
GST_INFO_OBJECT (clip, "Cannot move top effect %" GES_FORMAT
|
||||
" to index %i because the duration-limit cannot adjust",
|
||||
GES_ARGS (effect), newindex);
|
||||
return FALSE;
|
||||
|
@ -2707,9 +2731,28 @@ ges_clip_set_top_effect_index (GESClip * clip, GESBaseEffect * effect,
|
|||
}
|
||||
|
||||
/**
|
||||
* ges_clip_split:
|
||||
* ges_clip_set_top_effect_index:
|
||||
* @clip: A #GESClip
|
||||
* @effect: An effect within @clip to move
|
||||
* @newindex: The index for @effect in @clip
|
||||
*
|
||||
* See ges_clip_set_top_effect_index_full(), which also gives an error.
|
||||
*
|
||||
* Returns: %TRUE if @effect was successfully moved to @newindex.
|
||||
*/
|
||||
gboolean
|
||||
ges_clip_set_top_effect_index (GESClip * clip, GESBaseEffect * effect,
|
||||
guint newindex)
|
||||
{
|
||||
return ges_clip_set_top_effect_index_full (clip, effect, newindex, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* ges_clip_split_full:
|
||||
* @clip: The #GESClip to split
|
||||
* @position: The timeline position at which to perform the split
|
||||
* @position: The timeline position at which to perform the split, between
|
||||
* the start and end of the clip
|
||||
* @error: (nullable): Return location for an error
|
||||
*
|
||||
* Splits a clip at the given timeline position into two clips. The clip
|
||||
* must already have a #GESClip:layer.
|
||||
|
@ -2743,7 +2786,7 @@ ges_clip_set_top_effect_index (GESClip * clip, GESBaseEffect * effect,
|
|||
* from the splitting @clip, or %NULL if @clip can't be split.
|
||||
*/
|
||||
GESClip *
|
||||
ges_clip_split (GESClip * clip, guint64 position)
|
||||
ges_clip_split_full (GESClip * clip, guint64 position, GError ** error)
|
||||
{
|
||||
GList *tmp, *transitions = NULL;
|
||||
GESClip *new_object;
|
||||
|
@ -2752,10 +2795,12 @@ ges_clip_split (GESClip * clip, guint64 position)
|
|||
GESTimelineElement *element;
|
||||
GESTimeline *timeline;
|
||||
GHashTable *track_for_copy;
|
||||
guint32 layer_prio;
|
||||
|
||||
g_return_val_if_fail (GES_IS_CLIP (clip), NULL);
|
||||
g_return_val_if_fail (clip->priv->layer, NULL);
|
||||
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (position), NULL);
|
||||
g_return_val_if_fail (!error || !*error, NULL);
|
||||
|
||||
element = GES_TIMELINE_ELEMENT (clip);
|
||||
timeline = element->timeline;
|
||||
|
@ -2770,26 +2815,26 @@ ges_clip_split (GESClip * clip, guint64 position)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
layer_prio = ges_timeline_element_get_layer_priority (element);
|
||||
|
||||
old_duration = position - start;
|
||||
if (timeline && !timeline_tree_can_move_element (timeline_get_tree
|
||||
(timeline), element,
|
||||
ges_timeline_element_get_layer_priority (element),
|
||||
start, old_duration)) {
|
||||
GST_WARNING_OBJECT (clip,
|
||||
if (timeline
|
||||
&& !timeline_tree_can_move_element (timeline_get_tree (timeline), element,
|
||||
layer_prio, start, old_duration, error)) {
|
||||
GST_INFO_OBJECT (clip,
|
||||
"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),
|
||||
GST_TIME_ARGS (position));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
new_duration = duration + start - position;
|
||||
if (timeline && !timeline_tree_can_move_element (timeline_get_tree
|
||||
(timeline), element,
|
||||
ges_timeline_element_get_layer_priority (element),
|
||||
position, new_duration)) {
|
||||
GST_WARNING_OBJECT (clip,
|
||||
if (timeline
|
||||
&& !timeline_tree_can_move_element (timeline_get_tree (timeline), element,
|
||||
layer_prio, position, new_duration, error)) {
|
||||
GST_INFO_OBJECT (clip,
|
||||
"Can not split %" GES_FORMAT " at %" GST_TIME_FORMAT
|
||||
" as timeline would end up in an illegal" " state.", GES_ARGS (clip),
|
||||
" as timeline would be in an illegal state.", GES_ARGS (clip),
|
||||
GST_TIME_ARGS (position));
|
||||
return NULL;
|
||||
}
|
||||
|
@ -2879,6 +2924,22 @@ ges_clip_split (GESClip * clip, guint64 position)
|
|||
return new_object;
|
||||
}
|
||||
|
||||
/**
|
||||
* ges_clip_split:
|
||||
* @clip: The #GESClip to split
|
||||
* @position: The timeline position at which to perform the split
|
||||
*
|
||||
* See ges_clip_split_full(), which also gives an error.
|
||||
*
|
||||
* Returns: (transfer none) (nullable): The newly created clip resulting
|
||||
* from the splitting @clip, or %NULL if @clip can't be split.
|
||||
*/
|
||||
GESClip *
|
||||
ges_clip_split (GESClip * clip, guint64 position)
|
||||
{
|
||||
return ges_clip_split_full (clip, position, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* ges_clip_set_supported_formats:
|
||||
* @clip: A #GESClip
|
||||
|
@ -3073,7 +3134,7 @@ ges_clip_get_timeline_time_from_source_frame (GESClip * clip,
|
|||
* @clip: A #GESClip
|
||||
* @child: A child of @clip
|
||||
* @track: The track to add @child to
|
||||
* @err: Return location for an error
|
||||
* @error: (nullable): Return location for an error
|
||||
*
|
||||
* Adds the track element child of the clip to a specific track.
|
||||
*
|
||||
|
@ -3098,14 +3159,12 @@ ges_clip_get_timeline_time_from_source_frame (GESClip * clip,
|
|||
* such as causing three sources to overlap at a single time, or causing
|
||||
* a source to completely overlap another in the same track.
|
||||
*
|
||||
* Note that @err will not always be set upon failure.
|
||||
*
|
||||
* Returns: (transfer none): The element that was added to @track, either
|
||||
* @child or a copy of child, or %NULL if the element could not be added.
|
||||
*/
|
||||
GESTrackElement *
|
||||
ges_clip_add_child_to_track (GESClip * clip, GESTrackElement * child,
|
||||
GESTrack * track, GError ** err)
|
||||
GESTrack * track, GError ** error)
|
||||
{
|
||||
GESTimeline *timeline;
|
||||
GESTrackElement *el;
|
||||
|
@ -3114,7 +3173,7 @@ ges_clip_add_child_to_track (GESClip * clip, GESTrackElement * child,
|
|||
g_return_val_if_fail (GES_IS_CLIP (clip), NULL);
|
||||
g_return_val_if_fail (GES_IS_TRACK_ELEMENT (child), NULL);
|
||||
g_return_val_if_fail (GES_IS_TRACK (track), NULL);
|
||||
g_return_val_if_fail (!err || !*err, NULL);
|
||||
g_return_val_if_fail (!error || !*error, NULL);
|
||||
|
||||
timeline = GES_TIMELINE_ELEMENT_TIMELINE (clip);
|
||||
|
||||
|
@ -3164,11 +3223,8 @@ ges_clip_add_child_to_track (GESClip * clip, GESTrackElement * child,
|
|||
el = child;
|
||||
}
|
||||
|
||||
/* FIXME: set error if can not be added to track:
|
||||
* Either breaks the track rules for the clip, or the timeline
|
||||
* configuration rules */
|
||||
if (!ges_track_add_element (track, el)) {
|
||||
GST_WARNING_OBJECT (clip, "Could not add the track element %"
|
||||
if (!ges_track_add_element_full (track, el, error)) {
|
||||
GST_INFO_OBJECT (clip, "Could not add the track element %"
|
||||
GES_FORMAT " to the track %" GST_PTR_FORMAT, GES_ARGS (el), track);
|
||||
if (el != child)
|
||||
ges_container_remove (GES_CONTAINER (clip), GES_TIMELINE_ELEMENT (el));
|
||||
|
|
|
@ -145,48 +145,75 @@ struct _GESClipClass
|
|||
GES_API
|
||||
GESTrackType ges_clip_get_supported_formats (GESClip *clip);
|
||||
GES_API
|
||||
void ges_clip_set_supported_formats (GESClip *clip, GESTrackType supportedformats);
|
||||
void ges_clip_set_supported_formats (GESClip *clip,
|
||||
GESTrackType supportedformats);
|
||||
GES_API
|
||||
GESTrackElement* ges_clip_add_asset (GESClip *clip, GESAsset *asset);
|
||||
GESTrackElement* ges_clip_add_asset (GESClip *clip,
|
||||
GESAsset *asset);
|
||||
GES_API
|
||||
GESTrackElement* ges_clip_find_track_element (GESClip *clip, GESTrack *track,
|
||||
GESTrackElement* ges_clip_find_track_element (GESClip *clip,
|
||||
GESTrack *track,
|
||||
GType type);
|
||||
GES_API
|
||||
GList * ges_clip_find_track_elements (GESClip * clip, GESTrack * track,
|
||||
GESTrackType track_type, GType type);
|
||||
GList * ges_clip_find_track_elements (GESClip * clip,
|
||||
GESTrack * track,
|
||||
GESTrackType track_type,
|
||||
GType type);
|
||||
|
||||
GES_API
|
||||
GESTrackElement * ges_clip_add_child_to_track (GESClip * clip, GESTrackElement * child, GESTrack * track, GError **err);
|
||||
GESTrackElement * ges_clip_add_child_to_track (GESClip * clip,
|
||||
GESTrackElement * child,
|
||||
GESTrack * track,
|
||||
GError ** error);
|
||||
|
||||
/****************************************************
|
||||
* Layer *
|
||||
****************************************************/
|
||||
GES_API
|
||||
GESLayer* ges_clip_get_layer (GESClip *clip);
|
||||
GESLayer* ges_clip_get_layer (GESClip * clip);
|
||||
GES_API
|
||||
gboolean ges_clip_move_to_layer (GESClip *clip, GESLayer *layer);
|
||||
gboolean ges_clip_move_to_layer (GESClip * clip,
|
||||
GESLayer * layer);
|
||||
GES_API
|
||||
gboolean ges_clip_move_to_layer_full (GESClip * clip,
|
||||
GESLayer * layer,
|
||||
GError ** error);
|
||||
|
||||
/****************************************************
|
||||
* Effects *
|
||||
****************************************************/
|
||||
GES_API
|
||||
GList* ges_clip_get_top_effects (GESClip *clip);
|
||||
GList* ges_clip_get_top_effects (GESClip * clip);
|
||||
GES_API
|
||||
gint ges_clip_get_top_effect_position (GESClip *clip, GESBaseEffect *effect);
|
||||
gint ges_clip_get_top_effect_position (GESClip * clip,
|
||||
GESBaseEffect * effect);
|
||||
GES_API
|
||||
gint ges_clip_get_top_effect_index (GESClip *clip, GESBaseEffect *effect);
|
||||
gint ges_clip_get_top_effect_index (GESClip * clip,
|
||||
GESBaseEffect * effect);
|
||||
GES_API
|
||||
gboolean ges_clip_set_top_effect_priority (GESClip *clip, GESBaseEffect *effect,
|
||||
gboolean ges_clip_set_top_effect_priority (GESClip * clip,
|
||||
GESBaseEffect * effect,
|
||||
guint newpriority);
|
||||
GES_API
|
||||
gboolean ges_clip_set_top_effect_index (GESClip *clip, GESBaseEffect *effect,
|
||||
gboolean ges_clip_set_top_effect_index (GESClip * clip,
|
||||
GESBaseEffect * effect,
|
||||
guint newindex);
|
||||
GES_API
|
||||
gboolean ges_clip_set_top_effect_index_full (GESClip * clip,
|
||||
GESBaseEffect * effect,
|
||||
guint newindex,
|
||||
GError ** error);
|
||||
|
||||
/****************************************************
|
||||
* Editing *
|
||||
****************************************************/
|
||||
GES_API
|
||||
GESClip* ges_clip_split (GESClip *clip, guint64 position);
|
||||
GESClip* ges_clip_split (GESClip *clip,
|
||||
guint64 position);
|
||||
GES_API
|
||||
GESClip* ges_clip_split_full (GESClip *clip,
|
||||
guint64 position,
|
||||
GError ** error);
|
||||
|
||||
GES_API
|
||||
GstClockTime ges_clip_get_timeline_time_from_source_frame (GESClip * clip,
|
||||
|
|
|
@ -38,6 +38,17 @@ G_BEGIN_DECLS
|
|||
* @GES_ERROR_ASSET_WRONG_ID: The ID passed is malformed
|
||||
* @GES_ERROR_ASSET_LOADING: An error happened while loading the asset
|
||||
* @GES_ERROR_FORMATTER_MALFORMED_INPUT_FILE: The formatted files was malformed
|
||||
* @GES_ERROR_INVALID_FRAME_NUMBER: The frame number is invalid
|
||||
* @GES_ERROR_NEGATIVE_LAYER: The operation would lead to a negative
|
||||
* #GES_TIMELINE_ELEMENT_LAYER_PRIORITY
|
||||
* @GES_ERROR_NEGATIVE_TIME: The operation would lead to a negative time.
|
||||
* E.g. for the #GESTimelineElement:start #GESTimelineElement:duration or
|
||||
* #GESTimelineElement:in-point.
|
||||
* @GES_ERROR_NOT_ENOUGH_INTERNAL_CONTENT: Some #GESTimelineElement does
|
||||
* not have a large enough #GESTimelineElement:max-duration to cover the
|
||||
* desired operation
|
||||
* @GES_ERROR_INVALID_OVERLAP_IN_TRACK: The operation would break one of
|
||||
* the overlap conditions for the #GESTimeline
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
|
@ -45,6 +56,10 @@ typedef enum
|
|||
GES_ERROR_ASSET_LOADING,
|
||||
GES_ERROR_FORMATTER_MALFORMED_INPUT_FILE,
|
||||
GES_ERROR_INVALID_FRAME_NUMBER,
|
||||
GES_ERROR_NEGATIVE_LAYER,
|
||||
GES_ERROR_NEGATIVE_TIME,
|
||||
GES_ERROR_NOT_ENOUGH_INTERNAL_CONTENT,
|
||||
GES_ERROR_INVALID_OVERLAP_IN_TRACK,
|
||||
} GESError;
|
||||
|
||||
G_END_DECLS
|
||||
G_END_DECLS
|
||||
|
|
|
@ -222,7 +222,7 @@ _set_priority (GESTimelineElement * element, guint32 priority)
|
|||
|
||||
return timeline_tree_move (timeline_get_tree (timeline),
|
||||
element, (gint64) (element->priority) - (gint64) priority, 0,
|
||||
GES_EDGE_NONE, 0);
|
||||
GES_EDGE_NONE, 0, NULL);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
|
|
|
@ -61,6 +61,10 @@ GstDebugCategory * _ges_debug (void);
|
|||
#define _set_duration0 ges_timeline_element_set_duration
|
||||
#define _set_priority0 ges_timeline_element_set_priority
|
||||
|
||||
#define GES_CLOCK_TIME_IS_LESS(first, second) \
|
||||
(GST_CLOCK_TIME_IS_VALID (first) && (!GST_CLOCK_TIME_IS_VALID (second) \
|
||||
|| (first) < (second)))
|
||||
|
||||
#define DEFAULT_FRAMERATE_N 30
|
||||
#define DEFAULT_FRAMERATE_D 1
|
||||
#define DEFAULT_WIDTH 1280
|
||||
|
@ -110,8 +114,8 @@ G_GNUC_INTERNAL gboolean ges_timeline_is_disposed (GESTimeline* timeline);
|
|||
|
||||
G_GNUC_INTERNAL gboolean
|
||||
ges_timeline_edit (GESTimeline * timeline, GESTimelineElement * element,
|
||||
GList * layers, gint64 new_layer_priority, GESEditMode mode, GESEdge edge,
|
||||
guint64 position);
|
||||
gint64 new_layer_priority, GESEditMode mode, GESEdge edge,
|
||||
guint64 position, GError ** error);
|
||||
|
||||
G_GNUC_INTERNAL void
|
||||
timeline_add_group (GESTimeline *timeline,
|
||||
|
@ -152,7 +156,7 @@ G_GNUC_INTERNAL void
|
|||
ges_timeline_set_moving_track_elements (GESTimeline * timeline, gboolean moving);
|
||||
|
||||
G_GNUC_INTERNAL gboolean
|
||||
ges_timeline_add_clip (GESTimeline * timeline, GESClip * clip);
|
||||
ges_timeline_add_clip (GESTimeline * timeline, GESClip * clip, GError ** error);
|
||||
|
||||
G_GNUC_INTERNAL void
|
||||
ges_timeline_remove_clip (GESTimeline * timeline, GESClip * clip);
|
||||
|
@ -403,11 +407,11 @@ G_GNUC_INTERNAL gboolean ges_clip_is_moving_from_layer (GESClip *clip
|
|||
G_GNUC_INTERNAL void ges_clip_set_moving_from_layer (GESClip *clip, gboolean is_moving);
|
||||
G_GNUC_INTERNAL GESTrackElement* ges_clip_create_track_element (GESClip *clip, GESTrackType type);
|
||||
G_GNUC_INTERNAL GList* ges_clip_create_track_elements (GESClip *clip, GESTrackType type);
|
||||
G_GNUC_INTERNAL gboolean ges_clip_can_set_inpoint_of_child (GESClip * clip, GESTrackElement * child, GstClockTime inpoint);
|
||||
G_GNUC_INTERNAL gboolean ges_clip_can_set_max_duration_of_child (GESClip * clip, GESTrackElement * child, GstClockTime max_duration);
|
||||
G_GNUC_INTERNAL gboolean ges_clip_can_set_active_of_child (GESClip * clip, GESTrackElement * child, gboolean active);
|
||||
G_GNUC_INTERNAL gboolean ges_clip_can_set_priority_of_child (GESClip * clip, GESTrackElement * child, guint32 priority);
|
||||
G_GNUC_INTERNAL gboolean ges_clip_can_set_track_of_child (GESClip * clip, GESTrackElement * child, GESTrack * tack);
|
||||
G_GNUC_INTERNAL gboolean ges_clip_can_set_inpoint_of_child (GESClip * clip, GESTrackElement * child, GstClockTime inpoint, GError ** error);
|
||||
G_GNUC_INTERNAL gboolean ges_clip_can_set_max_duration_of_child (GESClip * clip, GESTrackElement * child, GstClockTime max_duration, GError ** error);
|
||||
G_GNUC_INTERNAL gboolean ges_clip_can_set_active_of_child (GESClip * clip, GESTrackElement * child, gboolean active, GError ** error);
|
||||
G_GNUC_INTERNAL gboolean ges_clip_can_set_priority_of_child (GESClip * clip, GESTrackElement * child, guint32 priority, GError ** error);
|
||||
G_GNUC_INTERNAL gboolean ges_clip_can_set_track_of_child (GESClip * clip, GESTrackElement * child, GESTrack * tack, GError ** error);
|
||||
G_GNUC_INTERNAL void ges_clip_empty_from_track (GESClip * clip, GESTrack * track);
|
||||
|
||||
/****************************************************
|
||||
|
@ -420,7 +424,7 @@ G_GNUC_INTERNAL void layer_set_priority (GESLayer * layer, guint p
|
|||
* GESTrackElement *
|
||||
****************************************************/
|
||||
#define NLE_OBJECT_TRACK_ELEMENT_QUARK (g_quark_from_string ("nle_object_track_element_quark"))
|
||||
G_GNUC_INTERNAL gboolean ges_track_element_set_track (GESTrackElement * object, GESTrack * track);
|
||||
G_GNUC_INTERNAL gboolean ges_track_element_set_track (GESTrackElement * object, GESTrack * track, GError ** error);
|
||||
G_GNUC_INTERNAL void ges_track_element_copy_properties (GESTimelineElement * element,
|
||||
GESTimelineElement * elementcopy);
|
||||
G_GNUC_INTERNAL void ges_track_element_set_layer_active (GESTrackElement *element, gboolean active);
|
||||
|
|
102
ges/ges-layer.c
102
ges/ges-layer.c
|
@ -237,24 +237,8 @@ ges_layer_class_init (GESLayerClass * klass)
|
|||
* GESLayer:auto-transition:
|
||||
*
|
||||
* Whether to automatically create a #GESTransitionClip whenever two
|
||||
* #GESClip-s overlap in the layer. Specifically, if this is set to
|
||||
* %TRUE, then wherever two #GESSource-s (that belong to some clip in
|
||||
* the layer) share the same #GESTrackElement:track and the end time of
|
||||
* one source exceeds the #GESTimelineElement:start time of the other,
|
||||
* there will exist a corresponding #GESTransitionClip in the same
|
||||
* layer. These automatic transitions will be created and removed
|
||||
* accordingly. Their temporal extent will cover the overlap of the
|
||||
* two sources (their #GESTimelineElement:start will be set to the
|
||||
* #GESTimelineElement:start of the later source, and their
|
||||
* #GESTimelineElement:duration will be the difference between the
|
||||
* #GESTimelineElement:start of the later source, and the end time of
|
||||
* the earlier source).
|
||||
*
|
||||
* It may be difficult to use this feature if your timeline has multiple
|
||||
* tracks of the same #GESTrack:track-type and you use the
|
||||
* #GESTimeline::select-tracks-for-object signal. You will have to
|
||||
* ensure that any #GESTransition that belongs to a newly created
|
||||
* transition clip is able to arrive in the correct track.
|
||||
* #GESSource-s that both belong to a #GESClip in the layer overlap.
|
||||
* See #GESTimeline for what counts as an overlap.
|
||||
*
|
||||
* When a layer is added to a #GESTimeline, if this property is left as
|
||||
* %FALSE, but the timeline's #GESTimeline:auto-transition is %TRUE, it
|
||||
|
@ -667,9 +651,10 @@ ges_layer_is_empty (GESLayer * layer)
|
|||
}
|
||||
|
||||
/**
|
||||
* ges_layer_add_clip:
|
||||
* ges_layer_add_clip_full:
|
||||
* @layer: The #GESLayer
|
||||
* @clip: (transfer floating): The clip to add
|
||||
* @error: (nullable): Return location for an error
|
||||
*
|
||||
* Adds the given clip to the layer. If the method succeeds, the layer
|
||||
* will take ownership of the clip.
|
||||
|
@ -682,17 +667,19 @@ ges_layer_is_empty (GESLayer * layer)
|
|||
* if @layer refused to add @clip.
|
||||
*/
|
||||
gboolean
|
||||
ges_layer_add_clip (GESLayer * layer, GESClip * clip)
|
||||
ges_layer_add_clip_full (GESLayer * layer, GESClip * clip, GError ** error)
|
||||
{
|
||||
GList *tmp, *prev_children;
|
||||
GList *tmp, *prev_children, *new_children;
|
||||
GESAsset *asset;
|
||||
GESLayerPrivate *priv;
|
||||
GESLayer *current_layer;
|
||||
GESTimeline *timeline;
|
||||
GESContainer *container;
|
||||
GError *timeline_error = NULL;
|
||||
|
||||
g_return_val_if_fail (GES_IS_LAYER (layer), FALSE);
|
||||
g_return_val_if_fail (GES_IS_CLIP (clip), FALSE);
|
||||
g_return_val_if_fail (!error || !*error, FALSE);
|
||||
|
||||
timeline = GES_TIMELINE_ELEMENT_TIMELINE (clip);
|
||||
container = GES_CONTAINER (clip);
|
||||
|
@ -786,9 +773,23 @@ ges_layer_add_clip (GESLayer * layer, GESClip * clip)
|
|||
|
||||
prev_children = ges_container_get_children (container, FALSE);
|
||||
|
||||
if (timeline && !ges_timeline_add_clip (timeline, clip)) {
|
||||
if (timeline && !ges_timeline_add_clip (timeline, clip, &timeline_error)) {
|
||||
GST_INFO_OBJECT (layer, "Could not add the clip %" GES_FORMAT
|
||||
" to the timeline %" GST_PTR_FORMAT, GES_ARGS (clip), timeline);
|
||||
|
||||
if (timeline_error) {
|
||||
if (error) {
|
||||
*error = timeline_error;
|
||||
} else {
|
||||
GST_WARNING_OBJECT (timeline, "Adding the clip %" GES_FORMAT
|
||||
" to the timeline failed: %s", GES_ARGS (clip),
|
||||
timeline_error->message);
|
||||
g_error_free (timeline_error);
|
||||
}
|
||||
}
|
||||
|
||||
/* remove any track elements that were newly created */
|
||||
GList *new_children = ges_container_get_children (container, FALSE);
|
||||
new_children = ges_container_get_children (container, FALSE);
|
||||
for (tmp = new_children; tmp; tmp = tmp->next) {
|
||||
if (!g_list_find (prev_children, tmp->data))
|
||||
ges_container_remove (container, tmp->data);
|
||||
|
@ -796,8 +797,6 @@ ges_layer_add_clip (GESLayer * layer, GESClip * clip)
|
|||
g_list_free_full (prev_children, gst_object_unref);
|
||||
g_list_free_full (new_children, gst_object_unref);
|
||||
|
||||
GST_WARNING_OBJECT (layer, "Could not add the clip %" GES_FORMAT
|
||||
" to the timeline %" GST_PTR_FORMAT, GES_ARGS (clip), timeline);
|
||||
/* FIXME: change emit signal to FALSE once we are able to delay the
|
||||
* "clip-added" signal until after ges_timeline_add_clip */
|
||||
ges_layer_remove_clip_internal (layer, clip, TRUE);
|
||||
|
@ -818,7 +817,23 @@ ges_layer_add_clip (GESLayer * layer, GESClip * clip)
|
|||
}
|
||||
|
||||
/**
|
||||
* ges_layer_add_asset:
|
||||
* ges_layer_add_clip:
|
||||
* @layer: The #GESLayer
|
||||
* @clip: (transfer floating): The clip to add
|
||||
*
|
||||
* See ges_layer_add_clip_full(), which also gives an error.
|
||||
*
|
||||
* Returns: %TRUE if @clip was properly added to @layer, or %FALSE
|
||||
* if @layer refused to add @clip.
|
||||
*/
|
||||
gboolean
|
||||
ges_layer_add_clip (GESLayer * layer, GESClip * clip)
|
||||
{
|
||||
return ges_layer_add_clip_full (layer, clip, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* ges_layer_add_asset_full:
|
||||
* @layer: The #GESLayer
|
||||
* @asset: The asset to extract the new clip from
|
||||
* @start: The #GESTimelineElement:start value to set on the new clip
|
||||
|
@ -830,6 +845,7 @@ ges_layer_add_clip (GESLayer * layer, GESClip * clip)
|
|||
* clip
|
||||
* @track_types: The #GESClip:supported-formats to set on the the new
|
||||
* clip, or #GES_TRACK_TYPE_UNKNOWN to use the default
|
||||
* @error: (nullable): Return location for an error
|
||||
*
|
||||
* Extracts a new clip from an asset and adds it to the layer with
|
||||
* the given properties.
|
||||
|
@ -837,14 +853,15 @@ ges_layer_add_clip (GESLayer * layer, GESClip * clip)
|
|||
* Returns: (transfer none): The newly created clip.
|
||||
*/
|
||||
GESClip *
|
||||
ges_layer_add_asset (GESLayer * layer,
|
||||
ges_layer_add_asset_full (GESLayer * layer,
|
||||
GESAsset * asset, GstClockTime start, GstClockTime inpoint,
|
||||
GstClockTime duration, GESTrackType track_types)
|
||||
GstClockTime duration, GESTrackType track_types, GError ** error)
|
||||
{
|
||||
GESClip *clip;
|
||||
|
||||
g_return_val_if_fail (GES_IS_LAYER (layer), NULL);
|
||||
g_return_val_if_fail (GES_IS_ASSET (asset), NULL);
|
||||
g_return_val_if_fail (!error || !*error, NULL);
|
||||
g_return_val_if_fail (g_type_is_a (ges_asset_get_extractable_type
|
||||
(asset), GES_TYPE_CLIP), NULL);
|
||||
|
||||
|
@ -873,13 +890,40 @@ ges_layer_add_asset (GESLayer * layer,
|
|||
_set_duration0 (GES_TIMELINE_ELEMENT (clip), duration);
|
||||
}
|
||||
|
||||
if (!ges_layer_add_clip (layer, clip)) {
|
||||
if (!ges_layer_add_clip_full (layer, clip, error)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return clip;
|
||||
}
|
||||
|
||||
/**
|
||||
* ges_layer_add_asset:
|
||||
* @layer: The #GESLayer
|
||||
* @asset: The asset to extract the new clip from
|
||||
* @start: The #GESTimelineElement:start value to set on the new clip
|
||||
* If `start == #GST_CLOCK_TIME_NONE`, it will be added to the end
|
||||
* of @layer, i.e. it will be set to @layer's duration
|
||||
* @inpoint: The #GESTimelineElement:in-point value to set on the new
|
||||
* clip
|
||||
* @duration: The #GESTimelineElement:duration value to set on the new
|
||||
* clip
|
||||
* @track_types: The #GESClip:supported-formats to set on the the new
|
||||
* clip, or #GES_TRACK_TYPE_UNKNOWN to use the default
|
||||
*
|
||||
* See ges_layer_add_asset_full(), which also gives an error.
|
||||
*
|
||||
* Returns: (transfer none): The newly created clip.
|
||||
*/
|
||||
GESClip *
|
||||
ges_layer_add_asset (GESLayer * layer,
|
||||
GESAsset * asset, GstClockTime start, GstClockTime inpoint,
|
||||
GstClockTime duration, GESTrackType track_types)
|
||||
{
|
||||
return ges_layer_add_asset_full (layer, asset, start, inpoint, duration,
|
||||
track_types, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* ges_layer_new:
|
||||
*
|
||||
|
|
|
@ -65,67 +65,82 @@ struct _GESLayerClass {
|
|||
|
||||
/*< private >*/
|
||||
/* Signals */
|
||||
void (*object_added) (GESLayer * layer, GESClip * object);
|
||||
void (*object_removed) (GESLayer * layer, GESClip * object);
|
||||
void (*object_added) (GESLayer * layer, GESClip * object);
|
||||
void (*object_removed) (GESLayer * layer, GESClip * object);
|
||||
|
||||
/* Padding for API extension */
|
||||
gpointer _ges_reserved[GES_PADDING];
|
||||
};
|
||||
|
||||
GES_API
|
||||
GESLayer* ges_layer_new (void);
|
||||
GESLayer* ges_layer_new (void);
|
||||
|
||||
GES_API
|
||||
void ges_layer_set_timeline (GESLayer * layer,
|
||||
GESTimeline * timeline);
|
||||
|
||||
GES_API GESTimeline *
|
||||
ges_layer_get_timeline (GESLayer * layer);
|
||||
void ges_layer_set_timeline (GESLayer * layer,
|
||||
GESTimeline * timeline);
|
||||
GES_API
|
||||
GESTimeline * ges_layer_get_timeline (GESLayer * layer);
|
||||
|
||||
GES_API
|
||||
gboolean ges_layer_add_clip (GESLayer * layer,
|
||||
GESClip * clip);
|
||||
gboolean ges_layer_add_clip (GESLayer * layer,
|
||||
GESClip * clip);
|
||||
GES_API
|
||||
GESClip * ges_layer_add_asset (GESLayer *layer,
|
||||
GESAsset *asset,
|
||||
GstClockTime start,
|
||||
GstClockTime inpoint,
|
||||
GstClockTime duration,
|
||||
GESTrackType track_types);
|
||||
gboolean ges_layer_add_clip_full (GESLayer * layer,
|
||||
GESClip * clip,
|
||||
GError ** error);
|
||||
GES_API
|
||||
GESClip * ges_layer_add_asset (GESLayer *layer,
|
||||
GESAsset *asset,
|
||||
GstClockTime start,
|
||||
GstClockTime inpoint,
|
||||
GstClockTime duration,
|
||||
GESTrackType track_types);
|
||||
GES_API
|
||||
GESClip * ges_layer_add_asset_full (GESLayer *layer,
|
||||
GESAsset *asset,
|
||||
GstClockTime start,
|
||||
GstClockTime inpoint,
|
||||
GstClockTime duration,
|
||||
GESTrackType track_types,
|
||||
GError ** error);
|
||||
|
||||
GES_API
|
||||
gboolean ges_layer_remove_clip (GESLayer * layer,
|
||||
GESClip * clip);
|
||||
gboolean ges_layer_remove_clip (GESLayer * layer,
|
||||
GESClip * clip);
|
||||
|
||||
GES_DEPRECATED_FOR(ges_timeline_move_layer)
|
||||
void ges_layer_set_priority (GESLayer * layer,
|
||||
guint priority);
|
||||
void ges_layer_set_priority (GESLayer * layer,
|
||||
guint priority);
|
||||
|
||||
GES_API
|
||||
gboolean ges_layer_is_empty (GESLayer * layer);
|
||||
gboolean ges_layer_is_empty (GESLayer * layer);
|
||||
|
||||
GES_API
|
||||
GList* ges_layer_get_clips_in_interval (GESLayer * layer, GstClockTime start, GstClockTime end);
|
||||
GList* ges_layer_get_clips_in_interval (GESLayer * layer,
|
||||
GstClockTime start,
|
||||
GstClockTime end);
|
||||
|
||||
GES_API
|
||||
guint ges_layer_get_priority (GESLayer * layer);
|
||||
guint ges_layer_get_priority (GESLayer * layer);
|
||||
|
||||
GES_API
|
||||
gboolean ges_layer_get_auto_transition (GESLayer * layer);
|
||||
gboolean ges_layer_get_auto_transition (GESLayer * layer);
|
||||
|
||||
GES_API
|
||||
void ges_layer_set_auto_transition (GESLayer * layer,
|
||||
gboolean auto_transition);
|
||||
void ges_layer_set_auto_transition (GESLayer * layer,
|
||||
gboolean auto_transition);
|
||||
|
||||
GES_API
|
||||
GList* ges_layer_get_clips (GESLayer * layer);
|
||||
GList* ges_layer_get_clips (GESLayer * layer);
|
||||
GES_API
|
||||
GstClockTime ges_layer_get_duration (GESLayer *layer);
|
||||
GstClockTime ges_layer_get_duration (GESLayer *layer);
|
||||
GES_API
|
||||
gboolean ges_layer_set_active_for_tracks (GESLayer *layer, gboolean active,
|
||||
GList *tracks);
|
||||
gboolean ges_layer_set_active_for_tracks (GESLayer *layer,
|
||||
gboolean active,
|
||||
GList *tracks);
|
||||
|
||||
GES_API gboolean ges_layer_get_active_for_track (GESLayer *layer,
|
||||
GESTrack *track);
|
||||
GES_API
|
||||
gboolean ges_layer_get_active_for_track (GESLayer *layer,
|
||||
GESTrack *track);
|
||||
|
||||
G_END_DECLS
|
||||
|
|
|
@ -48,12 +48,9 @@
|
|||
* similar changes in neighbouring or later elements in the timeline.
|
||||
*
|
||||
* However, a timeline may refuse a change in these properties if they
|
||||
* would place the timeline in an unsupported configuration. For example,
|
||||
* it is not possible for three #GESSourceClip-s in the same layer and
|
||||
* with the same track types to overlap at any given position in the
|
||||
* timeline (only two may overlap, which corresponds to a single
|
||||
* #GESTransition). Similarly, a #GESSourceClip may not entirely cover
|
||||
* another #GESSourceClip in the same layer and with the same track types.
|
||||
* would place the timeline in an unsupported configuration. See
|
||||
* #GESTimeline for its overlap rules.
|
||||
*
|
||||
* Additionally, an edit may be refused if it would place one of the
|
||||
* timing properties out of bounds (such as a negative time value for
|
||||
* #GESTimelineElement:start, or having insufficient internal
|
||||
|
@ -1153,8 +1150,7 @@ ges_timeline_element_set_inpoint (GESTimelineElement * self,
|
|||
if (G_UNLIKELY (inpoint == self->inpoint))
|
||||
return TRUE;
|
||||
|
||||
if (GST_CLOCK_TIME_IS_VALID (self->maxduration)
|
||||
&& inpoint > self->maxduration) {
|
||||
if (GES_CLOCK_TIME_IS_LESS (self->maxduration, inpoint)) {
|
||||
GST_WARNING_OBJECT (self, "Can not set an in-point of %" GST_TIME_FORMAT
|
||||
" because it exceeds the element's max-duration: %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (inpoint), GST_TIME_ARGS (self->maxduration));
|
||||
|
@ -1210,7 +1206,7 @@ ges_timeline_element_set_max_duration (GESTimelineElement * self,
|
|||
if (G_UNLIKELY (maxduration == self->maxduration))
|
||||
return TRUE;
|
||||
|
||||
if (GST_CLOCK_TIME_IS_VALID (maxduration) && self->inpoint > maxduration) {
|
||||
if (GES_CLOCK_TIME_IS_LESS (maxduration, self->inpoint)) {
|
||||
GST_WARNING_OBJECT (self, "Can not set a max-duration of %"
|
||||
GST_TIME_FORMAT " because it lies below the element's in-point: %"
|
||||
GST_TIME_FORMAT, GST_TIME_ARGS (maxduration),
|
||||
|
@ -2328,17 +2324,15 @@ ges_timeline_element_get_layer_priority (GESTimelineElement * self)
|
|||
}
|
||||
|
||||
/**
|
||||
* ges_timeline_element_edit:
|
||||
* ges_timeline_element_edit_full:
|
||||
* @self: The #GESTimelineElement to edit
|
||||
* @layers: (element-type GESLayer) (nullable): A whitelist of layers
|
||||
* where the edit can be performed, %NULL allows all layers in the
|
||||
* timeline
|
||||
* @new_layer_priority: The priority/index of the layer @self should be
|
||||
* moved to. -1 means no move
|
||||
* @mode: The edit mode
|
||||
* @edge: The edge of @self where the edit should occur
|
||||
* @position: The edit position: a new location for the edge of @self
|
||||
* (in nanoseconds)
|
||||
* @error: (nullable): Return location for an error
|
||||
*
|
||||
* Edits the element within its timeline by adjusting its
|
||||
* #GESTimelineElement:start, #GESTimelineElement:duration or
|
||||
|
@ -2363,28 +2357,20 @@ ges_timeline_element_get_layer_priority (GESTimelineElement * self)
|
|||
* the corresponding layer priority/index does not yet exist for the
|
||||
* timeline.
|
||||
*
|
||||
* @layers can be used as a whitelist to limit changes to elements that
|
||||
* exist in the corresponding layers. If you intend to also switch
|
||||
* elements between layers, then you must ensure that all the involved
|
||||
* layers are included for the switch to succeed (with the exception of
|
||||
* layers may be newly created).
|
||||
*
|
||||
* Returns: %TRUE if the edit of @self completed, %FALSE on failure.
|
||||
*
|
||||
* Since: 1.18
|
||||
*/
|
||||
|
||||
/* FIXME: handle the layers argument. Currently we always treat it as if
|
||||
* it is NULL in the ges-timeline code */
|
||||
gboolean
|
||||
ges_timeline_element_edit (GESTimelineElement * self, GList * layers,
|
||||
gint64 new_layer_priority, GESEditMode mode, GESEdge edge, guint64 position)
|
||||
ges_timeline_element_edit_full (GESTimelineElement * self,
|
||||
gint64 new_layer_priority, GESEditMode mode, GESEdge edge, guint64 position,
|
||||
GError ** error)
|
||||
{
|
||||
GESTimeline *timeline;
|
||||
guint32 layer_prio;
|
||||
|
||||
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 (!error || !*error, FALSE);
|
||||
|
||||
timeline = GES_TIMELINE_ELEMENT_TIMELINE (self);
|
||||
g_return_val_if_fail (timeline, FALSE);
|
||||
|
@ -2399,8 +2385,41 @@ ges_timeline_element_edit (GESTimelineElement * self, GList * layers,
|
|||
self->name, ges_edge_name (edge), GST_TIME_ARGS (position),
|
||||
ges_edit_mode_name (mode), new_layer_priority);
|
||||
|
||||
return ges_timeline_edit (timeline, self, layers, new_layer_priority, mode,
|
||||
edge, position);
|
||||
return ges_timeline_edit (timeline, self, new_layer_priority, mode,
|
||||
edge, position, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* ges_timeline_element_edit:
|
||||
* @self: The #GESTimelineElement to edit
|
||||
* @layers: (element-type GESLayer) (nullable): A whitelist of layers
|
||||
* where the edit can be performed, %NULL allows all layers in the
|
||||
* timeline.
|
||||
* @new_layer_priority: The priority/index of the layer @self should be
|
||||
* moved to. -1 means no move
|
||||
* @mode: The edit mode
|
||||
* @edge: The edge of @self where the edit should occur
|
||||
* @position: The edit position: a new location for the edge of @self
|
||||
* (in nanoseconds) in the timeline coordinates
|
||||
*
|
||||
* See ges_timeline_element_edit_full(), which also gives an error.
|
||||
*
|
||||
* Note that the @layers argument is currently ignored, so you should
|
||||
* just pass %NULL.
|
||||
*
|
||||
* Returns: %TRUE if the edit of @self completed, %FALSE on failure.
|
||||
*
|
||||
* Since: 1.18
|
||||
*/
|
||||
|
||||
/* FIXME: handle the layers argument. Currently we always treat it as if
|
||||
* it is NULL in the ges-timeline code */
|
||||
gboolean
|
||||
ges_timeline_element_edit (GESTimelineElement * self, GList * layers,
|
||||
gint64 new_layer_priority, GESEditMode mode, GESEdge edge, guint64 position)
|
||||
{
|
||||
return ges_timeline_element_edit_full (self, new_layer_priority, mode, edge,
|
||||
position, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -401,4 +401,12 @@ gboolean ges_timeline_element_edit (GESTimeli
|
|||
GESEditMode mode,
|
||||
GESEdge edge,
|
||||
guint64 position);
|
||||
|
||||
GES_API
|
||||
gboolean ges_timeline_element_edit_full (GESTimelineElement * self,
|
||||
gint64 new_layer_priority,
|
||||
GESEditMode mode,
|
||||
GESEdge edge,
|
||||
guint64 position,
|
||||
GError ** error);
|
||||
G_END_DECLS
|
||||
|
|
|
@ -79,6 +79,8 @@ struct _TreeIterationData
|
|||
{
|
||||
GNode *root;
|
||||
gboolean res;
|
||||
/* an error to set */
|
||||
GError **error;
|
||||
|
||||
/* The element we are visiting */
|
||||
GESTimelineElement *element;
|
||||
|
@ -299,7 +301,7 @@ _clock_time_plus (GstClockTime time, GstClockTime add)
|
|||
return GST_CLOCK_TIME_NONE;
|
||||
|
||||
if (time >= (G_MAXUINT64 - add)) {
|
||||
GST_INFO ("The time %" G_GUINT64_FORMAT " would overflow when "
|
||||
GST_ERROR ("The time %" G_GUINT64_FORMAT " would overflow when "
|
||||
"adding %" G_GUINT64_FORMAT, time, add);
|
||||
return GST_CLOCK_TIME_NONE;
|
||||
}
|
||||
|
@ -355,7 +357,7 @@ _abs_clock_time_distance (GstClockTime time1, GstClockTime time2)
|
|||
return time2 - time1;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
static void
|
||||
get_start_end_from_offset (GESTimelineElement * element, ElementEditMode mode,
|
||||
GstClockTimeDiff offset, GstClockTime * start, gboolean * negative_start,
|
||||
GstClockTime * end, gboolean * negative_end)
|
||||
|
@ -388,22 +390,6 @@ get_start_end_from_offset (GESTimelineElement * element, ElementEditMode mode,
|
|||
*start = new_start;
|
||||
if (end)
|
||||
*end = new_end;
|
||||
|
||||
if (start && !GST_CLOCK_TIME_IS_VALID (new_start)) {
|
||||
GST_INFO_OBJECT (element, "Cannot edit element %" GES_FORMAT " with "
|
||||
"offset %" G_GINT64_FORMAT " because it would result in an invalid "
|
||||
"start", GES_ARGS (element), offset);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (end && !GST_CLOCK_TIME_IS_VALID (new_end)) {
|
||||
GST_INFO_OBJECT (element, "Cannot edit element %" GES_FORMAT " with "
|
||||
"offset %" G_GINT64_FORMAT " because it would result in an invalid "
|
||||
"end", GES_ARGS (element), offset);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/****************************************************
|
||||
|
@ -571,9 +557,22 @@ timeline_tree_snap (GNode * root, GESTimelineElement * element,
|
|||
/* Allow negative start/end positions in case a snap makes them valid!
|
||||
* But we can still only snap to an existing edge in the timeline,
|
||||
* which should be a valid time */
|
||||
if (!get_start_end_from_offset (GES_TIMELINE_ELEMENT (source), mode,
|
||||
*offset, &start, &negative_start, &end, &negative_end))
|
||||
get_start_end_from_offset (GES_TIMELINE_ELEMENT (source), mode, *offset,
|
||||
&start, &negative_start, &end, &negative_end);
|
||||
|
||||
if (!GST_CLOCK_TIME_IS_VALID (start)) {
|
||||
GST_INFO_OBJECT (element, "Cannot edit element %" GES_FORMAT
|
||||
" with offset %" G_GINT64_FORMAT " because it would result in "
|
||||
"an invalid start", GES_ARGS (element), *offset);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!GST_CLOCK_TIME_IS_VALID (end)) {
|
||||
GST_INFO_OBJECT (element, "Cannot edit element %" GES_FORMAT
|
||||
" with offset %" G_GINT64_FORMAT " because it would result in "
|
||||
"an invalid end", GES_ARGS (element), *offset);
|
||||
goto done;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case EDIT_MOVE:
|
||||
|
@ -619,6 +618,41 @@ done:
|
|||
* Check Overlaps *
|
||||
****************************************************/
|
||||
|
||||
#define _SOURCE_FORMAT "\"%s\"%s%s%s"
|
||||
#define _SOURCE_ARGS(element) \
|
||||
element->name, element->parent ? " (parent: \"" : "", \
|
||||
element->parent ? element->parent->name : "", \
|
||||
element->parent ? "\")" : ""
|
||||
|
||||
static void
|
||||
set_full_overlap_error (GError ** error, GESTimelineElement * super,
|
||||
GESTimelineElement * sub, GESTrack * track)
|
||||
{
|
||||
if (error) {
|
||||
gchar *track_name = gst_object_get_name (GST_OBJECT (track));
|
||||
g_set_error (error, GES_ERROR, GES_ERROR_INVALID_OVERLAP_IN_TRACK,
|
||||
"The source " _SOURCE_FORMAT " would totally overlap the "
|
||||
"source " _SOURCE_FORMAT " in the track \"%s\"", _SOURCE_ARGS (super),
|
||||
_SOURCE_ARGS (sub), track_name);
|
||||
g_free (track_name);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
set_triple_overlap_error (GError ** error, GESTimelineElement * first,
|
||||
GESTimelineElement * second, GESTimelineElement * third, GESTrack * track)
|
||||
{
|
||||
if (error) {
|
||||
gchar *track_name = gst_object_get_name (GST_OBJECT (track));
|
||||
g_set_error (error, GES_ERROR, GES_ERROR_INVALID_OVERLAP_IN_TRACK,
|
||||
"The sources " _SOURCE_FORMAT ", " _SOURCE_FORMAT " and "
|
||||
_SOURCE_FORMAT " would all overlap at the same point in the "
|
||||
"track \"%s\"", _SOURCE_ARGS (first), _SOURCE_ARGS (second),
|
||||
_SOURCE_ARGS (third), track_name);
|
||||
g_free (track_name);
|
||||
}
|
||||
}
|
||||
|
||||
#define _ELEMENT_FORMAT \
|
||||
"%s (under %s) [%" GST_TIME_FORMAT " - %" GST_TIME_FORMAT "] " \
|
||||
"(layer: %" G_GUINT32_FORMAT ") (track :%" GST_PTR_FORMAT ")"
|
||||
|
@ -695,10 +729,19 @@ check_overlap_with_element (GNode * node, TreeIterationData * data)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
if ((cmp_start <= start && cmp_end >= end) ||
|
||||
(cmp_start >= start && cmp_end <= end)) {
|
||||
if (cmp_start <= start && cmp_end >= end) {
|
||||
/* cmp fully overlaps e */
|
||||
GST_INFO (_ELEMENT_FORMAT " and " _ELEMENT_FORMAT " fully overlap",
|
||||
_CMP_ARGS, _E_ARGS);
|
||||
set_full_overlap_error (data->error, cmp, e, track);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (cmp_start >= start && cmp_end <= end) {
|
||||
/* e fully overlaps cmp */
|
||||
GST_INFO (_ELEMENT_FORMAT " and " _ELEMENT_FORMAT " fully overlap",
|
||||
_CMP_ARGS, _E_ARGS);
|
||||
set_full_overlap_error (data->error, e, cmp, track);
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
@ -710,6 +753,8 @@ check_overlap_with_element (GNode * node, TreeIterationData * data)
|
|||
if (data->overlaping_on_start) {
|
||||
GST_INFO (_ELEMENT_FORMAT " is overlapped by %s and %s on its start",
|
||||
_CMP_ARGS, data->overlaping_on_start->name, e->name);
|
||||
set_triple_overlap_error (data->error, cmp, e, data->overlaping_on_start,
|
||||
track);
|
||||
goto error;
|
||||
}
|
||||
if (GST_CLOCK_TIME_IS_VALID (data->overlap_end_first_time) &&
|
||||
|
@ -717,6 +762,8 @@ check_overlap_with_element (GNode * node, TreeIterationData * data)
|
|||
GST_INFO (_ELEMENT_FORMAT " overlaps %s on its start and %s on its "
|
||||
"end, but they already overlap each other", _CMP_ARGS, e->name,
|
||||
data->overlaping_on_end->name);
|
||||
set_triple_overlap_error (data->error, cmp, e, data->overlaping_on_end,
|
||||
track);
|
||||
goto error;
|
||||
}
|
||||
/* record the time at which the overlapped ends */
|
||||
|
@ -733,6 +780,8 @@ check_overlap_with_element (GNode * node, TreeIterationData * data)
|
|||
if (data->overlaping_on_end) {
|
||||
GST_INFO (_ELEMENT_FORMAT " is overlapped by %s and %s on its end",
|
||||
_CMP_ARGS, data->overlaping_on_end->name, e->name);
|
||||
set_triple_overlap_error (data->error, cmp, e, data->overlaping_on_end,
|
||||
track);
|
||||
goto error;
|
||||
}
|
||||
if (GST_CLOCK_TIME_IS_VALID (data->overlap_start_final_time) &&
|
||||
|
@ -740,6 +789,8 @@ check_overlap_with_element (GNode * node, TreeIterationData * data)
|
|||
GST_INFO (_ELEMENT_FORMAT " overlaps %s on its end and %s on its "
|
||||
"start, but they already overlap each other", _CMP_ARGS, e->name,
|
||||
data->overlaping_on_start->name);
|
||||
set_triple_overlap_error (data->error, cmp, e, data->overlaping_on_start,
|
||||
track);
|
||||
goto error;
|
||||
}
|
||||
/* record the time at which the overlapped starts */
|
||||
|
@ -789,12 +840,14 @@ check_moving_overlaps (GNode * node, TreeIterationData * data)
|
|||
/* whether the elements in moving can be moved to their corresponding
|
||||
* PositionData */
|
||||
static gboolean
|
||||
timeline_tree_can_move_elements (GNode * root, GHashTable * moving)
|
||||
timeline_tree_can_move_elements (GNode * root, GHashTable * moving,
|
||||
GError ** error)
|
||||
{
|
||||
TreeIterationData data = tree_iteration_data_init;
|
||||
data.moving = moving;
|
||||
data.root = root;
|
||||
data.res = TRUE;
|
||||
data.error = error;
|
||||
/* sufficient to check the leaves, which is all the track elements or
|
||||
* empty clips
|
||||
* should also be sufficient to only check the moving elements */
|
||||
|
@ -808,8 +861,56 @@ timeline_tree_can_move_elements (GNode * root, GHashTable * moving)
|
|||
* Setting Edit Data *
|
||||
****************************************************/
|
||||
|
||||
static void
|
||||
set_negative_start_error (GError ** error, GESTimelineElement * element,
|
||||
GstClockTime neg_start)
|
||||
{
|
||||
g_set_error (error, GES_ERROR, GES_ERROR_NEGATIVE_TIME,
|
||||
"The element \"%s\" would have a negative start of -%"
|
||||
GST_TIME_FORMAT, element->name, GST_TIME_ARGS (neg_start));
|
||||
}
|
||||
|
||||
static void
|
||||
set_negative_duration_error (GError ** error, GESTimelineElement * element,
|
||||
GstClockTime neg_duration)
|
||||
{
|
||||
g_set_error (error, GES_ERROR, GES_ERROR_NEGATIVE_TIME,
|
||||
"The element \"%s\" would have a negative duration of -%"
|
||||
GST_TIME_FORMAT, element->name, GST_TIME_ARGS (neg_duration));
|
||||
}
|
||||
|
||||
static void
|
||||
set_negative_inpoint_error (GError ** error, GESTimelineElement * element,
|
||||
GstClockTime neg_inpoint)
|
||||
{
|
||||
g_set_error (error, GES_ERROR, GES_ERROR_NEGATIVE_TIME,
|
||||
"The element \"%s\" would have a negative in-point of -%"
|
||||
GST_TIME_FORMAT, element->name, GST_TIME_ARGS (neg_inpoint));
|
||||
}
|
||||
|
||||
static void
|
||||
set_negative_layer_error (GError ** error, GESTimelineElement * element,
|
||||
gint64 neg_layer)
|
||||
{
|
||||
g_set_error (error, GES_ERROR, GES_ERROR_NEGATIVE_LAYER,
|
||||
"The element \"%s\" would have a negative layer priority of -%"
|
||||
G_GINT64_FORMAT, element->name, neg_layer);
|
||||
}
|
||||
|
||||
static void
|
||||
set_breaks_duration_limit_error (GError ** error, GESClip * clip,
|
||||
GstClockTime duration, GstClockTime duration_limit)
|
||||
{
|
||||
g_set_error (error, GES_ERROR, GES_ERROR_NOT_ENOUGH_INTERNAL_CONTENT,
|
||||
"The clip \"%s\" would have a duration of %" GST_TIME_FORMAT
|
||||
" that would break its duration-limit of %" GST_TIME_FORMAT,
|
||||
GES_TIMELINE_ELEMENT_NAME (clip), GST_TIME_ARGS (duration),
|
||||
GST_TIME_ARGS (duration_limit));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
set_layer_priority (GESTimelineElement * element, EditData * data)
|
||||
set_layer_priority (GESTimelineElement * element, EditData * data,
|
||||
GError ** error)
|
||||
{
|
||||
gint64 layer_offset = data->layer_offset;
|
||||
guint32 layer_prio = ges_timeline_element_get_layer_priority (element);
|
||||
|
@ -827,10 +928,12 @@ set_layer_priority (GESTimelineElement * element, EditData * data)
|
|||
GST_INFO_OBJECT (element, "%s would have a negative layer priority (%"
|
||||
G_GUINT32_FORMAT " - %" G_GINT64_FORMAT ")", element->name,
|
||||
layer_prio, layer_offset);
|
||||
set_negative_layer_error (error, element,
|
||||
layer_offset - (gint64) layer_prio);
|
||||
return FALSE;
|
||||
}
|
||||
if ((layer_prio - (gint64) layer_offset) >= G_MAXUINT32) {
|
||||
GST_INFO_OBJECT (element, "%s would have an overflowing layer priority",
|
||||
GST_ERROR_OBJECT (element, "%s would have an overflowing layer priority",
|
||||
element->name);
|
||||
return FALSE;
|
||||
}
|
||||
|
@ -851,34 +954,45 @@ set_layer_priority (GESTimelineElement * element, EditData * data)
|
|||
}
|
||||
|
||||
static gboolean
|
||||
set_edit_move_values (GESTimelineElement * element, EditData * data)
|
||||
set_edit_move_values (GESTimelineElement * element, EditData * data,
|
||||
GError ** error)
|
||||
{
|
||||
gboolean negative = FALSE;
|
||||
GstClockTime new_start =
|
||||
_clock_time_minus_diff (element->start, data->offset, NULL);
|
||||
if (!GST_CLOCK_TIME_IS_VALID (new_start)) {
|
||||
_clock_time_minus_diff (element->start, data->offset, &negative);
|
||||
if (negative || !GST_CLOCK_TIME_IS_VALID (new_start)) {
|
||||
GST_INFO_OBJECT (element, "Cannot move %" GES_FORMAT " with offset %"
|
||||
G_GINT64_FORMAT " because it would result in an invalid start",
|
||||
GES_ARGS (element), data->offset);
|
||||
if (negative)
|
||||
set_negative_start_error (error, element, new_start);
|
||||
return FALSE;
|
||||
}
|
||||
_CHECK_END (element, new_start, element->duration);
|
||||
data->start = new_start;
|
||||
|
||||
if (GES_IS_GROUP (element))
|
||||
return TRUE;
|
||||
|
||||
GST_INFO_OBJECT (element, "%s will move by setting start to %"
|
||||
GST_TIME_FORMAT, element->name, GST_TIME_ARGS (data->start));
|
||||
|
||||
return set_layer_priority (element, data);
|
||||
return set_layer_priority (element, data, error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
set_edit_trim_start_inpoint_value (GESTimelineElement * element,
|
||||
EditData * data)
|
||||
EditData * data, GError ** error)
|
||||
{
|
||||
gboolean negative = FALSE;
|
||||
GstClockTime new_inpoint = _clock_time_minus_diff (element->inpoint,
|
||||
data->offset, NULL);
|
||||
if (!GST_CLOCK_TIME_IS_VALID (new_inpoint)) {
|
||||
data->offset, &negative);
|
||||
if (negative || !GST_CLOCK_TIME_IS_VALID (new_inpoint)) {
|
||||
GST_INFO_OBJECT (element, "Cannot trim start of %" GES_FORMAT
|
||||
" with offset %" G_GINT64_FORMAT " because it would result in an "
|
||||
"invalid in-point", GES_ARGS (element), data->offset);
|
||||
if (negative)
|
||||
set_negative_inpoint_error (error, element, new_inpoint);
|
||||
return FALSE;
|
||||
}
|
||||
data->inpoint = new_inpoint;
|
||||
|
@ -887,7 +1001,7 @@ set_edit_trim_start_inpoint_value (GESTimelineElement * element,
|
|||
|
||||
static gboolean
|
||||
set_edit_trim_start_non_core_children (GESTimelineElement * clip,
|
||||
GstClockTimeDiff offset, GHashTable * edit_table)
|
||||
GstClockTimeDiff offset, GHashTable * edit_table, GError ** error)
|
||||
{
|
||||
GList *tmp;
|
||||
GESTimelineElement *child;
|
||||
|
@ -916,7 +1030,7 @@ set_edit_trim_start_non_core_children (GESTimelineElement * clip,
|
|||
|
||||
data = new_edit_data (EDIT_TRIM_START, offset, 0);
|
||||
g_hash_table_insert (edit_table, child, data);
|
||||
if (!set_edit_trim_start_inpoint_value (child, data))
|
||||
if (!set_edit_trim_start_inpoint_value (child, data, error))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
@ -926,40 +1040,52 @@ set_edit_trim_start_non_core_children (GESTimelineElement * clip,
|
|||
/* trim the start of a clip or a track element */
|
||||
static gboolean
|
||||
set_edit_trim_start_values (GESTimelineElement * element, EditData * data,
|
||||
GHashTable * edit_table)
|
||||
GHashTable * edit_table, GError ** error)
|
||||
{
|
||||
gboolean negative = FALSE;
|
||||
GstClockTime new_duration;
|
||||
GstClockTime new_start =
|
||||
_clock_time_minus_diff (element->start, data->offset, NULL);
|
||||
GstClockTime new_duration =
|
||||
_clock_time_minus_diff (element->duration, -data->offset, NULL);
|
||||
_clock_time_minus_diff (element->start, data->offset, &negative);
|
||||
|
||||
if (!GST_CLOCK_TIME_IS_VALID (new_start)) {
|
||||
if (negative || !GST_CLOCK_TIME_IS_VALID (new_start)) {
|
||||
GST_INFO_OBJECT (element, "Cannot trim start of %" GES_FORMAT
|
||||
" with offset %" G_GINT64_FORMAT " because it would result in an "
|
||||
"invalid start", GES_ARGS (element), data->offset);
|
||||
if (negative)
|
||||
set_negative_start_error (error, element, new_start);
|
||||
return FALSE;
|
||||
}
|
||||
if (!GST_CLOCK_TIME_IS_VALID (new_duration)) {
|
||||
|
||||
new_duration =
|
||||
_clock_time_minus_diff (element->duration, -data->offset, &negative);
|
||||
|
||||
if (negative || !GST_CLOCK_TIME_IS_VALID (new_duration)) {
|
||||
GST_INFO_OBJECT (element, "Cannot trim start of %" GES_FORMAT
|
||||
" with offset %" G_GINT64_FORMAT " because it would result in an "
|
||||
"invalid duration", GES_ARGS (element), data->offset);
|
||||
if (negative)
|
||||
set_negative_duration_error (error, element, new_duration);
|
||||
return FALSE;
|
||||
}
|
||||
_CHECK_END (element, new_start, new_duration);
|
||||
|
||||
data->start = new_start;
|
||||
data->duration = new_duration;
|
||||
|
||||
if (GES_IS_GROUP (element))
|
||||
return TRUE;
|
||||
|
||||
if (GES_IS_CLIP (element)) {
|
||||
if (!set_edit_trim_start_inpoint_value (element, data))
|
||||
if (!set_edit_trim_start_inpoint_value (element, data, error))
|
||||
return FALSE;
|
||||
if (!set_edit_trim_start_non_core_children (element, data->offset,
|
||||
edit_table))
|
||||
edit_table, error))
|
||||
return FALSE;
|
||||
} else if (GES_IS_TRACK_ELEMENT (element)
|
||||
&& ges_track_element_has_internal_source (GES_TRACK_ELEMENT (element))) {
|
||||
if (!set_edit_trim_start_inpoint_value (element, data))
|
||||
if (!set_edit_trim_start_inpoint_value (element, data, error))
|
||||
return FALSE;
|
||||
}
|
||||
data->start = new_start;
|
||||
data->duration = new_duration;
|
||||
|
||||
/* NOTE: without time effects, the duration-limit will increase with
|
||||
* a decrease in in-point by the same amount that duration increases,
|
||||
|
@ -971,53 +1097,64 @@ set_edit_trim_start_values (GESTimelineElement * element, EditData * data,
|
|||
"to %" GST_TIME_FORMAT, element->name, GST_TIME_ARGS (data->start),
|
||||
GST_TIME_ARGS (data->inpoint), GST_TIME_ARGS (data->duration));
|
||||
|
||||
return set_layer_priority (element, data);
|
||||
return set_layer_priority (element, data, error);
|
||||
}
|
||||
|
||||
/* trim the end of a clip or a track element */
|
||||
static gboolean
|
||||
set_edit_trim_end_values (GESTimelineElement * element, EditData * data)
|
||||
set_edit_trim_end_values (GESTimelineElement * element, EditData * data,
|
||||
GError ** error)
|
||||
{
|
||||
gboolean negative = FALSE;
|
||||
GstClockTime new_duration =
|
||||
_clock_time_minus_diff (element->duration, data->offset, NULL);
|
||||
if (!GST_CLOCK_TIME_IS_VALID (new_duration)) {
|
||||
_clock_time_minus_diff (element->duration, data->offset, &negative);
|
||||
if (negative || !GST_CLOCK_TIME_IS_VALID (new_duration)) {
|
||||
GST_INFO_OBJECT (element, "Cannot trim end of %" GES_FORMAT
|
||||
" with offset %" G_GINT64_FORMAT " because it would result in an "
|
||||
"invalid duration", GES_ARGS (element), data->offset);
|
||||
if (negative)
|
||||
set_negative_duration_error (error, element, new_duration);
|
||||
return FALSE;
|
||||
}
|
||||
_CHECK_END (element, element->start, new_duration);
|
||||
|
||||
if (GES_IS_CLIP (element)) {
|
||||
GstClockTime limit = ges_clip_get_duration_limit (GES_CLIP (element));
|
||||
if (GST_CLOCK_TIME_IS_VALID (limit) && new_duration > limit) {
|
||||
GESClip *clip = GES_CLIP (element);
|
||||
GstClockTime limit = ges_clip_get_duration_limit (clip);
|
||||
|
||||
if (GES_CLOCK_TIME_IS_LESS (limit, new_duration)) {
|
||||
GST_INFO_OBJECT (element, "Cannot trim end of %" GES_FORMAT
|
||||
" with offset %" G_GINT64_FORMAT " because the duration would "
|
||||
"exceed the clip's duration-limit %" G_GINT64_FORMAT,
|
||||
GES_ARGS (element), data->offset, limit);
|
||||
|
||||
set_breaks_duration_limit_error (error, clip, new_duration, limit);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
data->duration = new_duration;
|
||||
|
||||
if (GES_IS_GROUP (element))
|
||||
return TRUE;
|
||||
|
||||
GST_INFO_OBJECT (element, "%s will trim end by setting duration to %"
|
||||
GST_TIME_FORMAT, element->name, GST_TIME_ARGS (data->duration));
|
||||
|
||||
return set_layer_priority (element, data);
|
||||
return set_layer_priority (element, data, error);
|
||||
}
|
||||
|
||||
/* handles clips and track elements with no parents */
|
||||
static gboolean
|
||||
set_clip_edit_values (GESTimelineElement * element, EditData * data,
|
||||
GHashTable * edit_table)
|
||||
set_edit_values (GESTimelineElement * element, EditData * data,
|
||||
GHashTable * edit_table, GError ** error)
|
||||
{
|
||||
switch (data->mode) {
|
||||
case EDIT_MOVE:
|
||||
return set_edit_move_values (element, data);
|
||||
return set_edit_move_values (element, data, error);
|
||||
case EDIT_TRIM_START:
|
||||
return set_edit_trim_start_values (element, data, edit_table);
|
||||
return set_edit_trim_start_values (element, data, edit_table, error);
|
||||
case EDIT_TRIM_END:
|
||||
return set_edit_trim_end_values (element, data);
|
||||
return set_edit_trim_end_values (element, data, error);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
@ -1041,7 +1178,7 @@ add_clips_to_list (GNode * node, GList ** list)
|
|||
|
||||
static gboolean
|
||||
replace_group_with_clip_edits (GNode * root, GESTimelineElement * group,
|
||||
GHashTable * edit_table)
|
||||
GHashTable * edit_table, GError ** err)
|
||||
{
|
||||
gboolean ret = TRUE;
|
||||
GList *tmp, *clips = NULL;
|
||||
|
@ -1064,13 +1201,26 @@ replace_group_with_clip_edits (GNode * root, GESTimelineElement * group,
|
|||
goto error;
|
||||
}
|
||||
|
||||
group_edit->start = group->start;
|
||||
group_edit->duration = group->duration;
|
||||
|
||||
/* should only set the start and duration fields, table should not be
|
||||
* needed, so we pass NULL */
|
||||
if (!set_edit_values (group, group_edit, NULL, err))
|
||||
goto error;
|
||||
|
||||
new_start = group_edit->start;
|
||||
new_end = _clock_time_plus (group_edit->start, group_edit->duration);
|
||||
|
||||
if (!GST_CLOCK_TIME_IS_VALID (new_start)
|
||||
|| !GST_CLOCK_TIME_IS_VALID (new_end)) {
|
||||
GST_ERROR_OBJECT (group, "Edit data gave an invalid start or end");
|
||||
goto error;
|
||||
}
|
||||
|
||||
layer_offset = group_edit->layer_offset;
|
||||
mode = group_edit->mode;
|
||||
|
||||
if (!get_start_end_from_offset (group, mode, group_edit->offset,
|
||||
&new_start, NULL, &new_end, NULL))
|
||||
goto error;
|
||||
|
||||
/* can traverse leaves to find all the clips since they are at _most_
|
||||
* one step above the track elements */
|
||||
g_node_traverse (node, G_IN_ORDER, G_TRAVERSE_LEAVES, -1,
|
||||
|
@ -1148,7 +1298,7 @@ replace_group_with_clip_edits (GNode * root, GESTimelineElement * group,
|
|||
}
|
||||
clip_data = new_edit_data (clip_mode, offset, layer_offset);
|
||||
g_hash_table_insert (edit_table, clip, clip_data);
|
||||
if (!set_clip_edit_values (clip, clip_data, edit_table))
|
||||
if (!set_edit_values (clip, clip_data, edit_table, err))
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
@ -1165,7 +1315,8 @@ error:
|
|||
/* set the edit values for the entries in @edits
|
||||
* any groups in @edits will be replaced by their clip children */
|
||||
static gboolean
|
||||
timeline_tree_set_element_edit_values (GNode * root, GHashTable * edits)
|
||||
timeline_tree_set_element_edit_values (GNode * root, GHashTable * edits,
|
||||
GError ** err)
|
||||
{
|
||||
gboolean ret = TRUE;
|
||||
GESTimelineElement *element;
|
||||
|
@ -1183,9 +1334,9 @@ timeline_tree_set_element_edit_values (GNode * root, GHashTable * edits)
|
|||
goto error;
|
||||
}
|
||||
if (GES_IS_GROUP (element))
|
||||
res = replace_group_with_clip_edits (root, element, edits);
|
||||
res = replace_group_with_clip_edits (root, element, edits, err);
|
||||
else
|
||||
res = set_clip_edit_values (element, edit_data, edits);
|
||||
res = set_edit_values (element, edit_data, edits, err);
|
||||
if (!res)
|
||||
goto error;
|
||||
}
|
||||
|
@ -1368,7 +1519,7 @@ add_element_edit (GHashTable * edits, GESTimelineElement * element,
|
|||
gboolean
|
||||
timeline_tree_can_move_element (GNode * root,
|
||||
GESTimelineElement * element, guint32 priority, GstClockTime start,
|
||||
GstClockTime duration)
|
||||
GstClockTime duration, GError ** error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
guint32 layer_prio = ges_timeline_element_get_layer_priority (element);
|
||||
|
@ -1431,10 +1582,10 @@ timeline_tree_can_move_element (GNode * root,
|
|||
|
||||
/* assume both edits can be performed if each could occur individually */
|
||||
/* should not effect duration or in-point */
|
||||
if (!timeline_tree_set_element_edit_values (root, move_edits))
|
||||
if (!timeline_tree_set_element_edit_values (root, move_edits, error))
|
||||
goto done;
|
||||
/* should not effect start or in-point or layer */
|
||||
if (!timeline_tree_set_element_edit_values (root, trim_edits))
|
||||
if (!timeline_tree_set_element_edit_values (root, trim_edits, error))
|
||||
goto done;
|
||||
|
||||
/* merge the two edits into moving positions */
|
||||
|
@ -1483,7 +1634,7 @@ timeline_tree_can_move_element (GNode * root,
|
|||
}
|
||||
|
||||
/* check overlaps */
|
||||
if (!timeline_tree_can_move_elements (root, moving))
|
||||
if (!timeline_tree_can_move_elements (root, moving, error))
|
||||
goto done;
|
||||
|
||||
ret = TRUE;
|
||||
|
@ -1624,7 +1775,7 @@ timeline_tree_perform_edits (GNode * root, GHashTable * edits)
|
|||
gboolean
|
||||
timeline_tree_ripple (GNode * root, GESTimelineElement * element,
|
||||
gint64 layer_priority_offset, GstClockTimeDiff offset, GESEdge edge,
|
||||
GstClockTime snapping_distance)
|
||||
GstClockTime snapping_distance, GError ** error)
|
||||
{
|
||||
gboolean res = TRUE;
|
||||
GNode *node;
|
||||
|
@ -1697,12 +1848,12 @@ timeline_tree_ripple (GNode * root, GESTimelineElement * element,
|
|||
|
||||
/* check and set edits using snapped values */
|
||||
give_edits_same_offset (edits, offset, layer_priority_offset);
|
||||
if (!timeline_tree_set_element_edit_values (root, edits))
|
||||
if (!timeline_tree_set_element_edit_values (root, edits, error))
|
||||
goto error;
|
||||
|
||||
/* check overlaps */
|
||||
set_moving_positions_from_edits (moving, edits);
|
||||
if (!timeline_tree_can_move_elements (root, moving))
|
||||
if (!timeline_tree_can_move_elements (root, moving, error))
|
||||
goto error;
|
||||
|
||||
/* emit snapping now. Edits should only fail if a programming error
|
||||
|
@ -1731,7 +1882,7 @@ error:
|
|||
gboolean
|
||||
timeline_tree_trim (GNode * root, GESTimelineElement * element,
|
||||
gint64 layer_priority_offset, GstClockTimeDiff offset, GESEdge edge,
|
||||
GstClockTime snapping_distance)
|
||||
GstClockTime snapping_distance, GError ** error)
|
||||
{
|
||||
gboolean res = TRUE;
|
||||
GHashTable *edits = new_edit_table ();
|
||||
|
@ -1778,12 +1929,12 @@ timeline_tree_trim (GNode * root, GESTimelineElement * element,
|
|||
|
||||
/* check and set edits using snapped values */
|
||||
give_edits_same_offset (edits, offset, layer_priority_offset);
|
||||
if (!timeline_tree_set_element_edit_values (root, edits))
|
||||
if (!timeline_tree_set_element_edit_values (root, edits, error))
|
||||
goto error;
|
||||
|
||||
/* check overlaps */
|
||||
set_moving_positions_from_edits (moving, edits);
|
||||
if (!timeline_tree_can_move_elements (root, moving)) {
|
||||
if (!timeline_tree_can_move_elements (root, moving, error)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
@ -1813,7 +1964,7 @@ error:
|
|||
gboolean
|
||||
timeline_tree_move (GNode * root, GESTimelineElement * element,
|
||||
gint64 layer_priority_offset, GstClockTimeDiff offset, GESEdge edge,
|
||||
GstClockTime snapping_distance)
|
||||
GstClockTime snapping_distance, GError ** error)
|
||||
{
|
||||
gboolean res = TRUE;
|
||||
GHashTable *edits = new_edit_table ();
|
||||
|
@ -1861,12 +2012,12 @@ timeline_tree_move (GNode * root, GESTimelineElement * element,
|
|||
|
||||
/* check and set edits using snapped values */
|
||||
give_edits_same_offset (edits, offset, layer_priority_offset);
|
||||
if (!timeline_tree_set_element_edit_values (root, edits))
|
||||
if (!timeline_tree_set_element_edit_values (root, edits, error))
|
||||
goto error;
|
||||
|
||||
/* check overlaps */
|
||||
set_moving_positions_from_edits (moving, edits);
|
||||
if (!timeline_tree_can_move_elements (root, moving)) {
|
||||
if (!timeline_tree_can_move_elements (root, moving, error)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
@ -1960,7 +2111,8 @@ find_sources_at_position (GNode * node, TreeIterationData * data)
|
|||
|
||||
gboolean
|
||||
timeline_tree_roll (GNode * root, GESTimelineElement * element,
|
||||
GstClockTimeDiff offset, GESEdge edge, GstClockTime snapping_distance)
|
||||
GstClockTimeDiff offset, GESEdge edge, GstClockTime snapping_distance,
|
||||
GError ** error)
|
||||
{
|
||||
gboolean res = TRUE;
|
||||
GList *tmp;
|
||||
|
@ -2043,12 +2195,12 @@ timeline_tree_roll (GNode * root, GESTimelineElement * element,
|
|||
|
||||
/* check and set edits using snapped values */
|
||||
give_edits_same_offset (edits, offset, 0);
|
||||
if (!timeline_tree_set_element_edit_values (root, edits))
|
||||
if (!timeline_tree_set_element_edit_values (root, edits, error))
|
||||
goto error;
|
||||
|
||||
/* check overlaps */
|
||||
set_moving_positions_from_edits (moving, edits);
|
||||
if (!timeline_tree_can_move_elements (root, moving)) {
|
||||
if (!timeline_tree_can_move_elements (root, moving, error)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,14 +13,16 @@ gboolean timeline_tree_can_move_element (GNode *root,
|
|||
GESTimelineElement *element,
|
||||
guint32 priority,
|
||||
GstClockTime start,
|
||||
GstClockTime duration);
|
||||
GstClockTime duration,
|
||||
GError ** error);
|
||||
|
||||
gboolean timeline_tree_ripple (GNode *root,
|
||||
GESTimelineElement *element,
|
||||
gint64 layer_priority_offset,
|
||||
GstClockTimeDiff offset,
|
||||
GESEdge edge,
|
||||
GstClockTime snapping_distance);
|
||||
GstClockTime snapping_distance,
|
||||
GError ** error);
|
||||
|
||||
void ges_timeline_emit_snapping (GESTimeline * timeline,
|
||||
GESTrackElement * elem1,
|
||||
|
@ -32,7 +34,8 @@ gboolean timeline_tree_trim (GNode *root,
|
|||
gint64 layer_priority_offset,
|
||||
GstClockTimeDiff offset,
|
||||
GESEdge edge,
|
||||
GstClockTime snapping_distance);
|
||||
GstClockTime snapping_distance,
|
||||
GError ** error);
|
||||
|
||||
|
||||
gboolean timeline_tree_move (GNode *root,
|
||||
|
@ -40,13 +43,15 @@ gboolean timeline_tree_move (GNode *root,
|
|||
gint64 layer_priority_offset,
|
||||
GstClockTimeDiff offset,
|
||||
GESEdge edge,
|
||||
GstClockTime snapping_distance);
|
||||
GstClockTime snapping_distance,
|
||||
GError ** error);
|
||||
|
||||
gboolean timeline_tree_roll (GNode * root,
|
||||
GESTimelineElement * element,
|
||||
GstClockTimeDiff offset,
|
||||
GESEdge edge,
|
||||
GstClockTime snapping_distance);
|
||||
GstClockTime snapping_distance,
|
||||
GError ** error);
|
||||
|
||||
typedef GESAutoTransition *
|
||||
(*GESTreeGetAutoTransitionFunc) (GESTimeline * timeline,
|
||||
|
|
|
@ -52,20 +52,74 @@
|
|||
* content prioritised in the tracks. This ordering can be changed using
|
||||
* ges_timeline_move_layer().
|
||||
*
|
||||
* ## Editing
|
||||
*
|
||||
* See #GESTimelineElement for the various ways the elements of a timeline
|
||||
* can be edited.
|
||||
*
|
||||
* If you change the timing or ordering of a timeline's
|
||||
* #GESTimelineElement-s, then these changes will not actually be taken
|
||||
* into account in the output of the timeline's tracks until the
|
||||
* ges_timeline_commit() method is called. This allows you to move its
|
||||
* elements around, say, in response to an end user's mouse dragging, with
|
||||
* little expense before finalising their effect on the produced data.
|
||||
*
|
||||
* ## Overlaps and Auto-Transitions
|
||||
*
|
||||
* There are certain restrictions placed on how #GESSource-s may overlap
|
||||
* in a #GESTrack that belongs to a timeline. These will be enforced by
|
||||
* GES, so the user will not need to keep track of them, but they should
|
||||
* be aware that certain edits will be refused as a result if the overlap
|
||||
* rules would be broken.
|
||||
*
|
||||
* Consider two #GESSource-s, `A` and `B`, with start times `startA` and
|
||||
* `startB`, and end times `endA` and `endB`, respectively. The start
|
||||
* time refers to their #GESTimelineElement:start, and the end time is
|
||||
* their #GESTimelineElement:start + #GESTimelineElement:duration. These
|
||||
* two sources *overlap* if:
|
||||
*
|
||||
* + they share the same #GESTrackElement:track (non %NULL), which belongs
|
||||
* to the timeline;
|
||||
* + they share the same #GES_TIMELINE_ELEMENT_LAYER_PRIORITY; and
|
||||
* + `startA < endB` and `startB < endA `.
|
||||
*
|
||||
* Note that when `startA = endB` or `startB = endA` then the two sources
|
||||
* will *touch* at their edges, but are not considered overlapping.
|
||||
*
|
||||
* If, in addition, `startA < startB < endA`, then we can say that the
|
||||
* end of `A` overlaps the start of `B`.
|
||||
*
|
||||
* If, instead, `startA <= startB` and `endA >= endB`, then we can say
|
||||
* that `A` fully overlaps `B`.
|
||||
*
|
||||
* The overlap rules for a timeline are that:
|
||||
*
|
||||
* 1. One source cannot fully overlap another source.
|
||||
* 2. A source can only overlap the end of up to one other source at its
|
||||
* start.
|
||||
* 3. A source can only overlap the start of up to one other source at its
|
||||
* end.
|
||||
*
|
||||
* The last two rules combined essentially mean that at any given timeline
|
||||
* position, only up to two #GESSource-s may overlap at that position. So
|
||||
* triple or more overlaps are not allowed.
|
||||
*
|
||||
* If you switch on #GESTimeline:auto-transition, then at any moment when
|
||||
* the end of one source (the first source) overlaps the start of another
|
||||
* (the second source), a #GESTransitionClip will be automatically created
|
||||
* for the pair in the same layer and it will cover their overlap. If the
|
||||
* two elements are edited in a way such that the end of the first source
|
||||
* no longer overlaps the start of the second, the transition will be
|
||||
* automatically removed from the timeline. However, if the two sources
|
||||
* still overlap at the same edges after the edit, then the same
|
||||
* transition object will be kept, but with its timing and layer adjusted
|
||||
* accordingly.
|
||||
*
|
||||
* ## Saving
|
||||
*
|
||||
* To save/load a timeline, you can use the ges_timeline_load_from_uri()
|
||||
* and ges_timeline_save_to_uri() methods that use the default format.
|
||||
*
|
||||
* ## Editing
|
||||
*
|
||||
* If you change the timing or ordering of a timeline's
|
||||
* #GESTimelineElement-s, then these changes will not actually be taken
|
||||
* into account in the timeline until the ges_timeline_commit() method is
|
||||
* called. This allows you to move its elements around, say, in
|
||||
* response to an end user's mouse dragging, with little expense before
|
||||
* finalising their effect.
|
||||
*
|
||||
* ## Playing
|
||||
*
|
||||
* A timeline is a #GstBin with a source #GstPad for each of its
|
||||
|
@ -152,7 +206,11 @@ struct _GESTimelinePrivate
|
|||
/* While we are creating and adding the TrackElements for a clip, we need to
|
||||
* ignore the child-added signal */
|
||||
gboolean track_elements_moving;
|
||||
gboolean track_selection_error;
|
||||
/* whether any error occurred during track selection, including
|
||||
* programming or usage errors */
|
||||
gboolean has_any_track_selection_error;
|
||||
/* error set for non-programming/usage errors */
|
||||
GError *track_selection_error;
|
||||
GList *groups;
|
||||
|
||||
guint stream_start_group_id;
|
||||
|
@ -355,6 +413,8 @@ ges_timeline_dispose (GObject * object)
|
|||
|
||||
gst_clear_object (&priv->auto_transition_track);
|
||||
gst_clear_object (&priv->new_track);
|
||||
g_clear_error (&priv->track_selection_error);
|
||||
priv->track_selection_error = NULL;
|
||||
|
||||
G_OBJECT_CLASS (ges_timeline_parent_class)->dispose (object);
|
||||
}
|
||||
|
@ -553,8 +613,10 @@ ges_timeline_class_init (GESTimelineClass * klass)
|
|||
/**
|
||||
* GESTimeline:auto-transition:
|
||||
*
|
||||
* Whether to automatically create a transition whenever two clips
|
||||
* overlap in the timeline. See #GESLayer:auto-transition.
|
||||
* Whether to automatically create a transition whenever two
|
||||
* #GESSource-s overlap in a track of the timeline. See
|
||||
* #GESLayer:auto-transition if you want this to only happen in some
|
||||
* layers.
|
||||
*/
|
||||
g_object_class_install_property (object_class, PROP_AUTO_TRANSITION,
|
||||
g_param_spec_boolean ("auto-transition", "Auto-Transition",
|
||||
|
@ -1168,8 +1230,8 @@ ges_timeline_freeze_auto_transitions (GESTimeline * timeline, gboolean freeze)
|
|||
|
||||
static gint
|
||||
_edit_auto_transition (GESTimeline * timeline, GESTimelineElement * element,
|
||||
GList * layers, gint64 new_layer_priority, GESEditMode mode, GESEdge edge,
|
||||
GstClockTime position)
|
||||
gint64 new_layer_priority, GESEditMode mode, GESEdge edge,
|
||||
GstClockTime position, GError ** error)
|
||||
{
|
||||
GList *tmp;
|
||||
guint32 layer_prio = ges_timeline_element_get_layer_priority (element);
|
||||
|
@ -1210,8 +1272,8 @@ _edit_auto_transition (GESTimeline * timeline, GESTimelineElement * element,
|
|||
|
||||
GST_INFO_OBJECT (element, "Trimming %" 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 ges_timeline_element_edit_full (replace, -1, mode, edge,
|
||||
position, error);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1220,8 +1282,8 @@ _edit_auto_transition (GESTimeline * timeline, GESTimelineElement * element,
|
|||
|
||||
gboolean
|
||||
ges_timeline_edit (GESTimeline * timeline, GESTimelineElement * element,
|
||||
GList * layers, gint64 new_layer_priority, GESEditMode mode, GESEdge edge,
|
||||
guint64 position)
|
||||
gint64 new_layer_priority, GESEditMode mode, GESEdge edge,
|
||||
guint64 position, GError ** error)
|
||||
{
|
||||
GstClockTimeDiff edge_diff = (edge == GES_EDGE_END ?
|
||||
GST_CLOCK_DIFF (position, element->start + element->duration) :
|
||||
|
@ -1231,8 +1293,8 @@ ges_timeline_edit (GESTimeline * timeline, GESTimelineElement * element,
|
|||
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);
|
||||
res = _edit_auto_transition (timeline, element, new_layer_priority, mode,
|
||||
edge, position, error);
|
||||
|
||||
if (res != -1)
|
||||
return res;
|
||||
|
@ -1240,20 +1302,20 @@ ges_timeline_edit (GESTimeline * timeline, GESTimelineElement * element,
|
|||
switch (mode) {
|
||||
case GES_EDIT_MODE_RIPPLE:
|
||||
return timeline_tree_ripple (timeline->priv->tree, element, prio_diff,
|
||||
edge_diff, edge, timeline->priv->snapping_distance);
|
||||
edge_diff, edge, timeline->priv->snapping_distance, error);
|
||||
case GES_EDIT_MODE_TRIM:
|
||||
return timeline_tree_trim (timeline->priv->tree, element, prio_diff,
|
||||
edge_diff, edge, timeline->priv->snapping_distance);
|
||||
edge_diff, edge, timeline->priv->snapping_distance, error);
|
||||
case GES_EDIT_MODE_NORMAL:
|
||||
return timeline_tree_move (timeline->priv->tree, element, prio_diff,
|
||||
edge_diff, edge, timeline->priv->snapping_distance);
|
||||
edge_diff, edge, timeline->priv->snapping_distance, error);
|
||||
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);
|
||||
edge_diff, edge, timeline->priv->snapping_distance, error);
|
||||
case GES_EDIT_MODE_SLIDE:
|
||||
GST_ERROR_OBJECT (element, "Sliding not implemented.");
|
||||
return FALSE;
|
||||
|
@ -1420,7 +1482,7 @@ _get_selected_tracks (GESTimeline * timeline, GESClip * clip,
|
|||
* selected tracks */
|
||||
static gboolean
|
||||
_add_track_element_to_tracks (GESTimeline * timeline, GESClip * clip,
|
||||
GESTrackElement * track_element)
|
||||
GESTrackElement * track_element, GError ** error)
|
||||
{
|
||||
guint i;
|
||||
gboolean ret = TRUE;
|
||||
|
@ -1428,8 +1490,11 @@ _add_track_element_to_tracks (GESTimeline * timeline, GESClip * clip,
|
|||
|
||||
for (i = 0; i < tracks->len; i++) {
|
||||
GESTrack *track = GES_TRACK (g_ptr_array_index (tracks, i));
|
||||
if (!ges_clip_add_child_to_track (clip, track_element, track, NULL))
|
||||
if (!ges_clip_add_child_to_track (clip, track_element, track, error)) {
|
||||
ret = FALSE;
|
||||
if (error)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_ptr_array_unref (tracks);
|
||||
|
@ -1439,7 +1504,7 @@ _add_track_element_to_tracks (GESTimeline * timeline, GESClip * clip,
|
|||
|
||||
static gboolean
|
||||
_try_add_track_element_to_track (GESTimeline * timeline, GESClip * clip,
|
||||
GESTrackElement * track_element, GESTrack * track)
|
||||
GESTrackElement * track_element, GESTrack * track, GError ** error)
|
||||
{
|
||||
gboolean no_error = TRUE;
|
||||
GPtrArray *tracks = _get_selected_tracks (timeline, clip, track_element);
|
||||
|
@ -1449,7 +1514,7 @@ _try_add_track_element_to_track (GESTimeline * timeline, GESClip * clip,
|
|||
* if it does we add the track element to the track, or add a copy if
|
||||
* the track element is already in a track */
|
||||
if (g_ptr_array_find (tracks, track, NULL)) {
|
||||
if (!ges_clip_add_child_to_track (clip, track_element, track, NULL))
|
||||
if (!ges_clip_add_child_to_track (clip, track_element, track, error))
|
||||
no_error = FALSE;
|
||||
}
|
||||
|
||||
|
@ -1469,21 +1534,46 @@ ges_timeline_set_moving_track_elements (GESTimeline * timeline, gboolean moving)
|
|||
}
|
||||
|
||||
static void
|
||||
_set_track_selection_error (GESTimeline * timeline, gboolean error)
|
||||
ges_timeline_set_track_selection_error (GESTimeline * timeline,
|
||||
gboolean was_error, GError * error)
|
||||
{
|
||||
GESTimelinePrivate *priv;
|
||||
|
||||
LOCK_DYN (timeline);
|
||||
timeline->priv->track_selection_error = error;
|
||||
|
||||
priv = timeline->priv;
|
||||
g_clear_error (&priv->track_selection_error);
|
||||
priv->track_selection_error = error;
|
||||
priv->has_any_track_selection_error = was_error;
|
||||
|
||||
UNLOCK_DYN (timeline);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_get_track_selection_error (GESTimeline * timeline)
|
||||
ges_timeline_take_track_selection_error (GESTimeline * timeline,
|
||||
GError ** error)
|
||||
{
|
||||
gboolean ret;
|
||||
GESTimelinePrivate *priv;
|
||||
|
||||
LOCK_DYN (timeline);
|
||||
ret = timeline->priv->track_selection_error;
|
||||
timeline->priv->track_selection_error = FALSE;
|
||||
|
||||
priv = timeline->priv;
|
||||
if (error) {
|
||||
if (*error) {
|
||||
GST_ERROR_OBJECT (timeline, "Error not handled %s", (*error)->message);
|
||||
g_error_free (*error);
|
||||
}
|
||||
*error = priv->track_selection_error;
|
||||
} else if (priv->track_selection_error) {
|
||||
GST_WARNING_OBJECT (timeline, "Got track selection error: %s",
|
||||
priv->track_selection_error->message);
|
||||
g_error_free (priv->track_selection_error);
|
||||
}
|
||||
priv->track_selection_error = NULL;
|
||||
ret = priv->has_any_track_selection_error;
|
||||
priv->has_any_track_selection_error = FALSE;
|
||||
|
||||
UNLOCK_DYN (timeline);
|
||||
|
||||
return ret;
|
||||
|
@ -1494,7 +1584,8 @@ clip_track_element_added_cb (GESClip * clip,
|
|||
GESTrackElement * track_element, GESTimeline * timeline)
|
||||
{
|
||||
GESTrack *auto_trans_track, *new_track;
|
||||
gboolean error = FALSE;
|
||||
GError *error = NULL;
|
||||
gboolean success = FALSE;
|
||||
|
||||
if (timeline->priv->track_elements_moving) {
|
||||
GST_DEBUG_OBJECT (timeline, "Ignoring element added: %" GES_FORMAT
|
||||
|
@ -1521,21 +1612,24 @@ clip_track_element_added_cb (GESClip * clip,
|
|||
|
||||
if (auto_trans_track) {
|
||||
/* don't use track-selection */
|
||||
if (!ges_clip_add_child_to_track (clip, track_element, auto_trans_track,
|
||||
NULL))
|
||||
error = TRUE;
|
||||
success = ! !ges_clip_add_child_to_track (clip, track_element,
|
||||
auto_trans_track, &error);
|
||||
gst_object_unref (auto_trans_track);
|
||||
} else {
|
||||
if (new_track)
|
||||
error =
|
||||
!_try_add_track_element_to_track (timeline, clip, track_element,
|
||||
new_track);
|
||||
success = _try_add_track_element_to_track (timeline, clip, track_element,
|
||||
new_track, &error);
|
||||
else
|
||||
error = !_add_track_element_to_tracks (timeline, clip, track_element);
|
||||
success = _add_track_element_to_tracks (timeline, clip, track_element,
|
||||
&error);
|
||||
}
|
||||
|
||||
if (error)
|
||||
_set_track_selection_error (timeline, TRUE);
|
||||
if (error || !success) {
|
||||
if (!error)
|
||||
GST_WARNING_OBJECT (timeline, "Track selection failed for %" GES_FORMAT,
|
||||
GES_ARGS (track_element));
|
||||
ges_timeline_set_track_selection_error (timeline, TRUE, error);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1573,7 +1667,7 @@ track_element_added_cb (GESTrack * track, GESTrackElement * element,
|
|||
/* returns TRUE if no errors in adding to tracks */
|
||||
static gboolean
|
||||
_add_clip_children_to_tracks (GESTimeline * timeline, GESClip * clip,
|
||||
gboolean add_core, GESTrack * new_track, GList * blacklist)
|
||||
gboolean add_core, GESTrack * new_track, GList * blacklist, GError ** error)
|
||||
{
|
||||
GList *tmp, *children;
|
||||
gboolean no_errors = TRUE;
|
||||
|
@ -1589,13 +1683,19 @@ _add_clip_children_to_tracks (GESTimeline * timeline, GESClip * clip,
|
|||
if (ges_track_element_get_track (el) == NULL) {
|
||||
gboolean res;
|
||||
if (new_track)
|
||||
res = _try_add_track_element_to_track (timeline, clip, el, new_track);
|
||||
res = _try_add_track_element_to_track (timeline, clip, el, new_track,
|
||||
error);
|
||||
else
|
||||
res = _add_track_element_to_tracks (timeline, clip, el);
|
||||
if (!res)
|
||||
res = _add_track_element_to_tracks (timeline, clip, el, error);
|
||||
if (!res) {
|
||||
no_errors = FALSE;
|
||||
if (error)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
g_list_free_full (children, gst_object_unref);
|
||||
|
||||
return no_errors;
|
||||
|
@ -1604,12 +1704,10 @@ _add_clip_children_to_tracks (GESTimeline * timeline, GESClip * clip,
|
|||
/* returns TRUE if no errors in adding to tracks */
|
||||
static gboolean
|
||||
add_object_to_tracks (GESTimeline * timeline, GESClip * clip,
|
||||
GESTrack * new_track)
|
||||
GESTrack * new_track, GError ** error)
|
||||
{
|
||||
GList *tracks, *tmp, *list, *created, *just_added = NULL;
|
||||
gboolean no_errors = TRUE;
|
||||
/* TODO: extend with GError ** argument, which is accepted by
|
||||
* ges_clip_add_child_to_track */
|
||||
|
||||
GST_DEBUG_OBJECT (timeline, "Creating %" GST_PTR_FORMAT
|
||||
" trackelements and adding them to our tracks", clip);
|
||||
|
@ -1619,12 +1717,19 @@ add_object_to_tracks (GESTimeline * timeline, GESClip * clip,
|
|||
g_list_copy_deep (timeline->tracks, (GCopyFunc) gst_object_ref, NULL);
|
||||
timeline->priv->new_track = new_track ? gst_object_ref (new_track) : NULL;
|
||||
UNLOCK_DYN (timeline);
|
||||
|
||||
/* create core elements */
|
||||
for (tmp = tracks; tmp; tmp = tmp->next) {
|
||||
GESTrack *track = GES_TRACK (tmp->data);
|
||||
if (new_track && track != new_track)
|
||||
continue;
|
||||
|
||||
list = ges_clip_create_track_elements (clip, track->type);
|
||||
/* just_added only used for pointer comparison, so safe to include
|
||||
* elements that may be destroyed because they fail to be added to
|
||||
* the clip */
|
||||
just_added = g_list_concat (just_added, list);
|
||||
|
||||
for (created = list; created; created = created->next) {
|
||||
GESTimelineElement *el = created->data;
|
||||
|
||||
|
@ -1640,21 +1745,25 @@ add_object_to_tracks (GESTimeline * timeline, GESClip * clip,
|
|||
* released. And when a child is selected for multiple tracks, their
|
||||
* copy will be added to the clip before the track is selected, so
|
||||
* the track will not be set in the child-added signal */
|
||||
_set_track_selection_error (timeline, FALSE);
|
||||
if (!ges_container_add (GES_CONTAINER (clip), el))
|
||||
ges_timeline_set_track_selection_error (timeline, FALSE, NULL);
|
||||
if (!ges_container_add (GES_CONTAINER (clip), el)) {
|
||||
no_errors = FALSE;
|
||||
GST_ERROR_OBJECT (clip, "Could not add the core element %s "
|
||||
"to the clip", el->name);
|
||||
if (_get_track_selection_error (timeline))
|
||||
no_errors = FALSE;
|
||||
|
||||
}
|
||||
gst_object_unref (el);
|
||||
|
||||
if (error && !no_errors)
|
||||
goto done;
|
||||
|
||||
if (ges_timeline_take_track_selection_error (timeline, error)) {
|
||||
no_errors = FALSE;
|
||||
if (error)
|
||||
goto done;
|
||||
/* else, carry on as much as we can */
|
||||
}
|
||||
}
|
||||
/* just_added only used for pointer comparison, so safe to include
|
||||
* elements that are now destroyed because they failed to be added to
|
||||
* the clip */
|
||||
just_added = g_list_concat (just_added, list);
|
||||
}
|
||||
g_list_free_full (tracks, gst_object_unref);
|
||||
|
||||
/* set the tracks for the other children, with core elements first to
|
||||
* make sure the non-core can be placed above them in the track (a
|
||||
|
@ -1662,11 +1771,21 @@ add_object_to_tracks (GESTimeline * timeline, GESClip * clip,
|
|||
/* include just_added as a blacklist to ensure we do not try the track
|
||||
* selection a second time when track selection returns no tracks */
|
||||
if (!_add_clip_children_to_tracks (timeline, clip, TRUE, new_track,
|
||||
just_added))
|
||||
just_added, error)) {
|
||||
no_errors = FALSE;
|
||||
if (error)
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!_add_clip_children_to_tracks (timeline, clip, FALSE, new_track,
|
||||
just_added))
|
||||
just_added, error)) {
|
||||
no_errors = FALSE;
|
||||
if (error)
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
g_list_free_full (tracks, gst_object_unref);
|
||||
|
||||
LOCK_DYN (timeline);
|
||||
gst_clear_object (&timeline->priv->new_track);
|
||||
|
@ -1721,12 +1840,10 @@ layer_auto_transition_changed_cb (GESLayer * layer,
|
|||
|
||||
/* returns TRUE if selecting of tracks did not error */
|
||||
gboolean
|
||||
ges_timeline_add_clip (GESTimeline * timeline, GESClip * clip)
|
||||
ges_timeline_add_clip (GESTimeline * timeline, GESClip * clip, GError ** error)
|
||||
{
|
||||
GESProject *project;
|
||||
gboolean ret;
|
||||
/* TODO: extend with GError ** argument, which is accepted by
|
||||
* ges_clip_add_child_to_track */
|
||||
|
||||
ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (clip), timeline);
|
||||
|
||||
|
@ -1754,7 +1871,7 @@ ges_timeline_add_clip (GESTimeline * timeline, GESClip * clip)
|
|||
/* timeline-tree handles creation of auto-transitions */
|
||||
ret = TRUE;
|
||||
} else {
|
||||
ret = add_object_to_tracks (timeline, clip, NULL);
|
||||
ret = add_object_to_tracks (timeline, clip, NULL, error);
|
||||
}
|
||||
|
||||
GST_DEBUG ("Done");
|
||||
|
@ -2215,7 +2332,7 @@ ges_timeline_add_layer (GESTimeline * timeline, GESLayer * layer)
|
|||
/* add any existing clips to the timeline */
|
||||
objects = ges_layer_get_clips (layer);
|
||||
for (tmp = objects; tmp; tmp = tmp->next)
|
||||
ges_timeline_add_clip (timeline, tmp->data);
|
||||
ges_timeline_add_clip (timeline, tmp->data, NULL);
|
||||
g_list_free_full (objects, gst_object_unref);
|
||||
|
||||
return TRUE;
|
||||
|
@ -2361,7 +2478,7 @@ ges_timeline_add_track (GESTimeline * timeline, GESTrack * track)
|
|||
objects = ges_layer_get_clips (tmp->data);
|
||||
|
||||
for (obj = objects; obj; obj = obj->next)
|
||||
add_object_to_tracks (timeline, obj->data, track);
|
||||
add_object_to_tracks (timeline, obj->data, track, NULL);
|
||||
|
||||
g_list_free_full (objects, gst_object_unref);
|
||||
}
|
||||
|
|
|
@ -630,6 +630,7 @@ _set_inpoint (GESTimelineElement * element, GstClockTime inpoint)
|
|||
{
|
||||
GESTrackElement *object = GES_TRACK_ELEMENT (element);
|
||||
GESTimelineElement *parent = element->parent;
|
||||
GError *error = NULL;
|
||||
|
||||
g_return_val_if_fail (object->priv->nleobject, FALSE);
|
||||
if (inpoint && !object->priv->has_internal_source) {
|
||||
|
@ -640,10 +641,12 @@ _set_inpoint (GESTimelineElement * element, GstClockTime inpoint)
|
|||
|
||||
if (GES_IS_CLIP (parent)
|
||||
&& !ges_clip_can_set_inpoint_of_child (GES_CLIP (parent), object,
|
||||
inpoint)) {
|
||||
inpoint, &error)) {
|
||||
GST_WARNING_OBJECT (element, "Cannot set an in-point of %"
|
||||
GST_TIME_FORMAT " because the parent clip %" GES_FORMAT
|
||||
" would not allow it", GST_TIME_ARGS (inpoint), GES_ARGS (parent));
|
||||
" would not allow it%s%s", GST_TIME_ARGS (inpoint),
|
||||
GES_ARGS (parent), error ? ": " : "", error ? error->message : "");
|
||||
g_clear_error (&error);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
@ -678,6 +681,7 @@ _set_max_duration (GESTimelineElement * element, GstClockTime max_duration)
|
|||
{
|
||||
GESTrackElement *object = GES_TRACK_ELEMENT (element);
|
||||
GESTimelineElement *parent = element->parent;
|
||||
GError *error = NULL;
|
||||
|
||||
if (GST_CLOCK_TIME_IS_VALID (max_duration)
|
||||
&& !object->priv->has_internal_source) {
|
||||
|
@ -688,10 +692,12 @@ _set_max_duration (GESTimelineElement * element, GstClockTime max_duration)
|
|||
|
||||
if (GES_IS_CLIP (parent)
|
||||
&& !ges_clip_can_set_max_duration_of_child (GES_CLIP (parent), object,
|
||||
max_duration)) {
|
||||
max_duration, &error)) {
|
||||
GST_WARNING_OBJECT (element, "Cannot set a max-duration of %"
|
||||
GST_TIME_FORMAT " because the parent clip %" GES_FORMAT
|
||||
" would not allow it", GST_TIME_ARGS (max_duration), GES_ARGS (parent));
|
||||
" would not allow it%s%s", GST_TIME_ARGS (max_duration),
|
||||
GES_ARGS (parent), error ? ": " : "", error ? error->message : "");
|
||||
g_clear_error (&error);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
@ -704,6 +710,7 @@ _set_priority (GESTimelineElement * element, guint32 priority)
|
|||
{
|
||||
GESTrackElement *object = GES_TRACK_ELEMENT (element);
|
||||
GESTimelineElement *parent = element->parent;
|
||||
GError *error = NULL;
|
||||
|
||||
g_return_val_if_fail (object->priv->nleobject, FALSE);
|
||||
|
||||
|
@ -720,10 +727,12 @@ _set_priority (GESTimelineElement * element, guint32 priority)
|
|||
|
||||
if (GES_IS_CLIP (parent)
|
||||
&& !ges_clip_can_set_priority_of_child (GES_CLIP (parent), object,
|
||||
priority)) {
|
||||
priority, &error)) {
|
||||
GST_WARNING_OBJECT (element, "Cannot set a priority of %"
|
||||
G_GUINT32_FORMAT " because the parent clip %" GES_FORMAT
|
||||
" would not allow it", priority, GES_ARGS (parent));
|
||||
" would not allow it%s%s", priority, GES_ARGS (parent),
|
||||
error ? ": " : "", error ? error->message : "");
|
||||
g_clear_error (&error);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
@ -751,6 +760,7 @@ gboolean
|
|||
ges_track_element_set_active (GESTrackElement * object, gboolean active)
|
||||
{
|
||||
GESTimelineElement *parent;
|
||||
GError *error = NULL;
|
||||
g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), FALSE);
|
||||
g_return_val_if_fail (object->priv->nleobject, FALSE);
|
||||
|
||||
|
@ -761,9 +771,13 @@ ges_track_element_set_active (GESTrackElement * object, gboolean active)
|
|||
|
||||
parent = GES_TIMELINE_ELEMENT_PARENT (object);
|
||||
if (GES_IS_CLIP (parent)
|
||||
&& !ges_clip_can_set_active_of_child (GES_CLIP (parent), object, active)) {
|
||||
GST_WARNING_OBJECT (object, "Cannot set active to %i because the parent "
|
||||
"clip %" GES_FORMAT " would not allow it", active, GES_ARGS (parent));
|
||||
&& !ges_clip_can_set_active_of_child (GES_CLIP (parent), object, active,
|
||||
&error)) {
|
||||
GST_WARNING_OBJECT (object,
|
||||
"Cannot set active to %i because the parent clip %" GES_FORMAT
|
||||
" would not allow it%s%s", active, GES_ARGS (parent), error ? ": " : "",
|
||||
error ? error->message : "");
|
||||
g_clear_error (&error);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
@ -1057,7 +1071,8 @@ ges_track_element_add_children_props (GESTrackElement * self,
|
|||
|
||||
/* INTERNAL USAGE */
|
||||
gboolean
|
||||
ges_track_element_set_track (GESTrackElement * object, GESTrack * track)
|
||||
ges_track_element_set_track (GESTrackElement * object, GESTrack * track,
|
||||
GError ** error)
|
||||
{
|
||||
GESTimelineElement *parent = GES_TIMELINE_ELEMENT_PARENT (object);
|
||||
|
||||
|
@ -1066,10 +1081,11 @@ ges_track_element_set_track (GESTrackElement * object, GESTrack * track)
|
|||
GST_DEBUG_OBJECT (object, "new track: %" GST_PTR_FORMAT, track);
|
||||
|
||||
if (GES_IS_CLIP (parent)
|
||||
&& !ges_clip_can_set_track_of_child (GES_CLIP (parent), object, track)) {
|
||||
GST_WARNING_OBJECT (object, "The parent clip %" GES_FORMAT " would "
|
||||
"not allow the track to be set to %" GST_PTR_FORMAT,
|
||||
GES_ARGS (parent), track);
|
||||
&& !ges_clip_can_set_track_of_child (GES_CLIP (parent), object, track,
|
||||
error)) {
|
||||
GST_INFO_OBJECT (object,
|
||||
"The parent clip %" GES_FORMAT " would not allow the track to be "
|
||||
"set to %" GST_PTR_FORMAT, GES_ARGS (parent), track);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
|
|
@ -383,7 +383,7 @@ ges_track_get_composition (GESTrack * track)
|
|||
*/
|
||||
static gboolean
|
||||
remove_object_internal (GESTrack * track, GESTrackElement * object,
|
||||
gboolean emit)
|
||||
gboolean emit, GError ** error)
|
||||
{
|
||||
GESTrackPrivate *priv;
|
||||
GstElement *nleobject;
|
||||
|
@ -397,8 +397,8 @@ remove_object_internal (GESTrack * track, GESTrackElement * object,
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
if (!ges_track_element_set_track (object, NULL)) {
|
||||
GST_WARNING_OBJECT (track, "Failed to unset the track for %" GES_FORMAT,
|
||||
if (!ges_track_element_set_track (object, NULL, error)) {
|
||||
GST_INFO_OBJECT (track, "Failed to unset the track for %" GES_FORMAT,
|
||||
GES_ARGS (object));
|
||||
return FALSE;
|
||||
}
|
||||
|
@ -426,7 +426,7 @@ remove_object_internal (GESTrack * track, GESTrackElement * object,
|
|||
static void
|
||||
dispose_trackelements_foreach (GESTrackElement * trackelement, GESTrack * track)
|
||||
{
|
||||
remove_object_internal (track, trackelement, TRUE);
|
||||
remove_object_internal (track, trackelement, TRUE, NULL);
|
||||
}
|
||||
|
||||
/* GstElement virtual methods */
|
||||
|
@ -1110,7 +1110,7 @@ notify:
|
|||
|
||||
static gboolean
|
||||
remove_element_internal (GESTrack * track, GESTrackElement * object,
|
||||
gboolean emit)
|
||||
gboolean emit, GError ** error)
|
||||
{
|
||||
GSequenceIter *it;
|
||||
GESTrackPrivate *priv = track->priv;
|
||||
|
@ -1120,7 +1120,7 @@ remove_element_internal (GESTrack * track, GESTrackElement * object,
|
|||
it = g_hash_table_lookup (priv->trackelements_iter, object);
|
||||
g_sequence_remove (it);
|
||||
|
||||
if (remove_object_internal (track, object, emit) == TRUE) {
|
||||
if (remove_object_internal (track, object, emit, error) == TRUE) {
|
||||
ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (object), NULL);
|
||||
|
||||
return TRUE;
|
||||
|
@ -1134,9 +1134,10 @@ remove_element_internal (GESTrack * track, GESTrackElement * object,
|
|||
}
|
||||
|
||||
/**
|
||||
* ges_track_add_element:
|
||||
* ges_track_add_element_full:
|
||||
* @track: A #GESTrack
|
||||
* @object: (transfer floating): The element to add
|
||||
* @error: (nullable): Return location for an error
|
||||
*
|
||||
* Adds the given track element to the track, which takes ownership of the
|
||||
* element.
|
||||
|
@ -1149,13 +1150,15 @@ remove_element_internal (GESTrack * track, GESTrackElement * object,
|
|||
* Returns: %TRUE if @object was successfully added to @track.
|
||||
*/
|
||||
gboolean
|
||||
ges_track_add_element (GESTrack * track, GESTrackElement * object)
|
||||
ges_track_add_element_full (GESTrack * track, GESTrackElement * object,
|
||||
GError ** error)
|
||||
{
|
||||
GESTimeline *timeline;
|
||||
GESTimelineElement *el;
|
||||
|
||||
g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
|
||||
g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), FALSE);
|
||||
g_return_val_if_fail (!error || !*error, FALSE);
|
||||
|
||||
el = GES_TIMELINE_ELEMENT (object);
|
||||
|
||||
|
@ -1170,8 +1173,8 @@ ges_track_add_element (GESTrack * track, GESTrackElement * object)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
if (!ges_track_element_set_track (object, track)) {
|
||||
GST_WARNING_OBJECT (track, "Failed to set the track for %" GES_FORMAT,
|
||||
if (!ges_track_element_set_track (object, track, error)) {
|
||||
GST_INFO_OBJECT (track, "Failed to set the track for %" GES_FORMAT,
|
||||
GES_ARGS (object));
|
||||
gst_object_ref_sink (object);
|
||||
gst_object_unref (object);
|
||||
|
@ -1186,7 +1189,9 @@ ges_track_add_element (GESTrack * track, GESTrackElement * object)
|
|||
if (G_UNLIKELY (!ges_nle_composition_add_object (track->priv->composition,
|
||||
ges_track_element_get_nleobject (object)))) {
|
||||
GST_WARNING ("Couldn't add object to the NleComposition");
|
||||
ges_track_element_set_track (object, NULL);
|
||||
if (!ges_track_element_set_track (object, NULL, NULL))
|
||||
GST_ERROR_OBJECT (track, "Failed to unset track of element %"
|
||||
GES_FORMAT, GES_ARGS (object));
|
||||
gst_object_ref_sink (object);
|
||||
gst_object_unref (object);
|
||||
return FALSE;
|
||||
|
@ -1203,12 +1208,13 @@ ges_track_add_element (GESTrack * track, GESTrackElement * object)
|
|||
* element to the track */
|
||||
if (timeline
|
||||
&& !timeline_tree_can_move_element (timeline_get_tree (timeline), el,
|
||||
GES_TIMELINE_ELEMENT_LAYER_PRIORITY (el), el->start, el->duration)) {
|
||||
GST_WARNING_OBJECT (track,
|
||||
GES_TIMELINE_ELEMENT_LAYER_PRIORITY (el), el->start, el->duration,
|
||||
error)) {
|
||||
GST_INFO_OBJECT (track,
|
||||
"Could not add the track element %" GES_FORMAT
|
||||
" to the track because it breaks the timeline " "configuration rules",
|
||||
GES_ARGS (el));
|
||||
remove_element_internal (track, object, FALSE);
|
||||
remove_element_internal (track, object, FALSE, NULL);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
@ -1218,6 +1224,21 @@ ges_track_add_element (GESTrack * track, GESTrackElement * object)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ges_track_add_element:
|
||||
* @track: A #GESTrack
|
||||
* @object: (transfer floating): The element to add
|
||||
*
|
||||
* See ges_track_add_element(), which also gives an error.
|
||||
*
|
||||
* Returns: %TRUE if @object was successfully added to @track.
|
||||
*/
|
||||
gboolean
|
||||
ges_track_add_element (GESTrack * track, GESTrackElement * object)
|
||||
{
|
||||
return ges_track_add_element_full (track, object, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* ges_track_get_elements:
|
||||
* @track: A #GESTrack
|
||||
|
@ -1245,9 +1266,10 @@ ges_track_get_elements (GESTrack * track)
|
|||
}
|
||||
|
||||
/**
|
||||
* ges_track_remove_element:
|
||||
* ges_track_remove_element_full:
|
||||
* @track: A #GESTrack
|
||||
* @object: The element to remove
|
||||
* @error: (nullable): Return location for an error
|
||||
*
|
||||
* Removes the given track element from the track, which revokes
|
||||
* ownership of the element.
|
||||
|
@ -1255,16 +1277,33 @@ ges_track_get_elements (GESTrack * track)
|
|||
* Returns: %TRUE if @object was successfully removed from @track.
|
||||
*/
|
||||
gboolean
|
||||
ges_track_remove_element (GESTrack * track, GESTrackElement * object)
|
||||
ges_track_remove_element_full (GESTrack * track, GESTrackElement * object,
|
||||
GError ** error)
|
||||
{
|
||||
g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
|
||||
g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), FALSE);
|
||||
g_return_val_if_fail (!error || !*error, FALSE);
|
||||
|
||||
if (!track->priv->timeline
|
||||
|| !ges_timeline_is_disposed (track->priv->timeline))
|
||||
CHECK_THREAD (track);
|
||||
|
||||
return remove_element_internal (track, object, TRUE);
|
||||
return remove_element_internal (track, object, TRUE, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* ges_track_remove_element:
|
||||
* @track: A #GESTrack
|
||||
* @object: The element to remove
|
||||
*
|
||||
* See ges_track_remove_element_full(), which also returns an error.
|
||||
*
|
||||
* Returns: %TRUE if @object was successfully removed from @track.
|
||||
*/
|
||||
gboolean
|
||||
ges_track_remove_element (GESTrack * track, GESTrackElement * object)
|
||||
{
|
||||
return ges_track_remove_element_full (track, object, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -83,25 +83,41 @@ const GESTimeline* ges_track_get_timeline (GESTrack *track);
|
|||
GES_API
|
||||
gboolean ges_track_commit (GESTrack *track);
|
||||
GES_API
|
||||
void ges_track_set_timeline (GESTrack *track, GESTimeline *timeline);
|
||||
void ges_track_set_timeline (GESTrack *track,
|
||||
GESTimeline *timeline);
|
||||
GES_API
|
||||
gboolean ges_track_add_element (GESTrack *track, GESTrackElement *object);
|
||||
gboolean ges_track_add_element (GESTrack *track,
|
||||
GESTrackElement *object);
|
||||
GES_API
|
||||
gboolean ges_track_remove_element (GESTrack *track, GESTrackElement *object);
|
||||
gboolean ges_track_add_element_full (GESTrack *track,
|
||||
GESTrackElement *object,
|
||||
GError ** error);
|
||||
GES_API
|
||||
void ges_track_set_create_element_for_gap_func (GESTrack *track, GESCreateElementForGapFunc func);
|
||||
gboolean ges_track_remove_element (GESTrack *track,
|
||||
GESTrackElement *object);
|
||||
GES_API
|
||||
void ges_track_set_mixing (GESTrack *track, gboolean mixing);
|
||||
gboolean ges_track_remove_element_full (GESTrack *track,
|
||||
GESTrackElement *object,
|
||||
GError ** error);
|
||||
GES_API
|
||||
void ges_track_set_create_element_for_gap_func (GESTrack *track,
|
||||
GESCreateElementForGapFunc func);
|
||||
GES_API
|
||||
void ges_track_set_mixing (GESTrack *track,
|
||||
gboolean mixing);
|
||||
GES_API
|
||||
gboolean ges_track_get_mixing (GESTrack *track);
|
||||
GES_API
|
||||
void ges_track_set_restriction_caps (GESTrack *track, const GstCaps *caps);
|
||||
void ges_track_set_restriction_caps (GESTrack *track,
|
||||
const GstCaps *caps);
|
||||
GES_API
|
||||
void ges_track_update_restriction_caps (GESTrack *track, const GstCaps *caps);
|
||||
void ges_track_update_restriction_caps (GESTrack *track,
|
||||
const GstCaps *caps);
|
||||
GES_API
|
||||
GstCaps * ges_track_get_restriction_caps (GESTrack * track);
|
||||
|
||||
GES_API
|
||||
GESTrack* ges_track_new (GESTrackType type, GstCaps * caps);
|
||||
GESTrack* ges_track_new (GESTrackType type,
|
||||
GstCaps * caps);
|
||||
|
||||
G_END_DECLS
|
||||
|
|
|
@ -1249,6 +1249,7 @@ GST_START_TEST (test_adding_children_to_track)
|
|||
GESTrackElement *source, *effect, *effect2, *added, *added2, *added3;
|
||||
GstControlSource *ctrl_source;
|
||||
guint selection_called = 0;
|
||||
GError *error = NULL;
|
||||
|
||||
ges_init ();
|
||||
|
||||
|
@ -1257,7 +1258,6 @@ GST_START_TEST (test_adding_children_to_track)
|
|||
track1 = GES_TRACK (ges_video_track_new ());
|
||||
track2 = GES_TRACK (ges_video_track_new ());
|
||||
|
||||
|
||||
/* only add two for now */
|
||||
fail_unless (ges_timeline_add_track (timeline, track1));
|
||||
|
||||
|
@ -1325,18 +1325,21 @@ GST_START_TEST (test_adding_children_to_track)
|
|||
gst_object_unref (ctrl_source);
|
||||
|
||||
/* can't add to a track that does not belong to the timeline */
|
||||
fail_if (ges_clip_add_child_to_track (clip, source, track2, NULL));
|
||||
fail_if (ges_clip_add_child_to_track (clip, source, track2, &error));
|
||||
assert_num_children (clip, 3);
|
||||
fail_unless (ges_track_element_get_track (source) == track1);
|
||||
assert_num_in_track (track1, 3);
|
||||
assert_num_in_track (track2, 0);
|
||||
/* programming/usage error gives no error code/message */
|
||||
fail_if (error);
|
||||
|
||||
/* can't add the clip to a track that already contains our source */
|
||||
fail_if (ges_clip_add_child_to_track (clip, source, track1, NULL));
|
||||
fail_if (ges_clip_add_child_to_track (clip, source, track1, &error));
|
||||
assert_num_children (clip, 3);
|
||||
fail_unless (ges_track_element_get_track (source) == track1);
|
||||
assert_num_in_track (track1, 3);
|
||||
assert_num_in_track (track2, 0);
|
||||
fail_if (error);
|
||||
|
||||
/* can't remove a core element from its track whilst a non-core sits
|
||||
* above it */
|
||||
|
@ -1347,10 +1350,11 @@ GST_START_TEST (test_adding_children_to_track)
|
|||
assert_num_in_track (track2, 0);
|
||||
|
||||
/* can not add to the same track as it is currently in */
|
||||
fail_if (ges_clip_add_child_to_track (clip, effect, track1, NULL));
|
||||
fail_if (ges_clip_add_child_to_track (clip, effect, track1, &error));
|
||||
fail_unless (ges_track_element_get_track (effect) == track1);
|
||||
assert_num_in_track (track1, 3);
|
||||
assert_num_in_track (track2, 0);
|
||||
fail_if (error);
|
||||
|
||||
/* adding another video track, select-tracks-for-object will do nothing
|
||||
* since no each track element is already part of a track */
|
||||
|
@ -1360,14 +1364,15 @@ GST_START_TEST (test_adding_children_to_track)
|
|||
assert_num_in_track (track2, 0);
|
||||
|
||||
/* can not add effect to a track that does not contain a core child */
|
||||
fail_if (ges_clip_add_child_to_track (clip, effect, track2, NULL));
|
||||
fail_if (ges_clip_add_child_to_track (clip, effect, track2, &error));
|
||||
assert_num_children (clip, 3);
|
||||
assert_num_in_track (track1, 3);
|
||||
assert_num_in_track (track2, 0);
|
||||
fail_if (error);
|
||||
|
||||
/* can add core */
|
||||
|
||||
added = ges_clip_add_child_to_track (clip, source, track2, NULL);
|
||||
added = ges_clip_add_child_to_track (clip, source, track2, &error);
|
||||
fail_unless (added);
|
||||
assert_num_children (clip, 4);
|
||||
fail_unless (added != source);
|
||||
|
@ -1375,6 +1380,7 @@ GST_START_TEST (test_adding_children_to_track)
|
|||
fail_unless (ges_track_element_get_track (added) == track2);
|
||||
assert_num_in_track (track1, 3);
|
||||
assert_num_in_track (track2, 1);
|
||||
fail_if (error);
|
||||
|
||||
assert_equal_children_properties (added, source);
|
||||
assert_equal_bindings (added, source);
|
||||
|
@ -1385,8 +1391,9 @@ GST_START_TEST (test_adding_children_to_track)
|
|||
assert_equals_int (1,
|
||||
ges_clip_get_top_effect_index (clip, GES_BASE_EFFECT (effect2)));
|
||||
|
||||
added2 = ges_clip_add_child_to_track (clip, effect, track2, NULL);
|
||||
added2 = ges_clip_add_child_to_track (clip, effect, track2, &error);
|
||||
fail_unless (added2);
|
||||
fail_if (error);
|
||||
assert_num_children (clip, 5);
|
||||
fail_unless (added2 != effect);
|
||||
fail_unless (ges_track_element_get_track (effect) == track1);
|
||||
|
@ -1404,8 +1411,9 @@ GST_START_TEST (test_adding_children_to_track)
|
|||
assert_equals_int (2,
|
||||
ges_clip_get_top_effect_index (clip, GES_BASE_EFFECT (effect2)));
|
||||
|
||||
added3 = ges_clip_add_child_to_track (clip, effect2, track2, NULL);
|
||||
added3 = ges_clip_add_child_to_track (clip, effect2, track2, &error);
|
||||
fail_unless (added3);
|
||||
fail_if (error);
|
||||
assert_num_children (clip, 6);
|
||||
fail_unless (added3 != effect2);
|
||||
fail_unless (ges_track_element_get_track (effect2) == track1);
|
||||
|
@ -1500,36 +1508,41 @@ GST_START_TEST (test_adding_children_to_track)
|
|||
|
||||
/* can not add the source to the track because it would overlap another
|
||||
* source */
|
||||
fail_if (ges_clip_add_child_to_track (clip, source, track1, NULL));
|
||||
fail_if (ges_clip_add_child_to_track (clip, source, track1, &error));
|
||||
assert_num_children (clip, 3);
|
||||
assert_num_in_track (track1, 4);
|
||||
assert_GESError (error, GES_ERROR_INVALID_OVERLAP_IN_TRACK);
|
||||
|
||||
/* can not add source at time 23 because it would result in three
|
||||
* overlapping sources in the track */
|
||||
assert_set_start (clip, 23);
|
||||
fail_if (ges_clip_add_child_to_track (clip, source, track1, NULL));
|
||||
fail_if (ges_clip_add_child_to_track (clip, source, track1, &error));
|
||||
assert_num_children (clip, 3);
|
||||
assert_num_in_track (track1, 4);
|
||||
assert_GESError (error, GES_ERROR_INVALID_OVERLAP_IN_TRACK);
|
||||
|
||||
/* can add at 5, with overlap */
|
||||
assert_set_start (clip, 5);
|
||||
added = ges_clip_add_child_to_track (clip, source, track1, NULL);
|
||||
added = ges_clip_add_child_to_track (clip, source, track1, &error);
|
||||
/* added is the source since it was not already in a track */
|
||||
fail_unless (added == source);
|
||||
fail_if (error);
|
||||
assert_num_children (clip, 3);
|
||||
/* 4 sources + 2 transitions */
|
||||
assert_num_in_track (track1, 6);
|
||||
|
||||
/* also add effect */
|
||||
added = ges_clip_add_child_to_track (clip, effect, track1, NULL);
|
||||
added = ges_clip_add_child_to_track (clip, effect, track1, &error);
|
||||
/* added is the source since it was not already in a track */
|
||||
fail_unless (added == effect);
|
||||
fail_if (error);
|
||||
assert_num_children (clip, 3);
|
||||
assert_num_in_track (track1, 7);
|
||||
|
||||
added = ges_clip_add_child_to_track (clip, effect2, track1, NULL);
|
||||
added = ges_clip_add_child_to_track (clip, effect2, track1, &error);
|
||||
/* added is the source since it was not already in a track */
|
||||
fail_unless (added == effect2);
|
||||
fail_if (error);
|
||||
assert_num_children (clip, 3);
|
||||
assert_num_in_track (track1, 8);
|
||||
|
||||
|
@ -3071,6 +3084,7 @@ GST_START_TEST (test_can_set_duration_limit)
|
|||
GESTrackElement *effect0, *effect1, *effect2;
|
||||
GESTrack *track0, *track1;
|
||||
gint limit_notify_count = 0;
|
||||
GError *error = NULL;
|
||||
|
||||
ges_init ();
|
||||
|
||||
|
@ -3175,7 +3189,8 @@ GST_START_TEST (test_can_set_duration_limit)
|
|||
CHECK_OBJECT_PROPS_MAX (source1, 10, 16, 20, 36);
|
||||
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 10);
|
||||
|
||||
fail_if (ges_clip_add_child_to_track (clip, effect0, track0, NULL));
|
||||
fail_if (ges_clip_add_child_to_track (clip, effect0, track0, &error));
|
||||
assert_GESError (error, GES_ERROR_INVALID_OVERLAP_IN_TRACK);
|
||||
|
||||
/* set max-duration to 11 and we are fine to select a track */
|
||||
assert_set_max_duration (effect0, 11);
|
||||
|
@ -3183,7 +3198,8 @@ GST_START_TEST (test_can_set_duration_limit)
|
|||
_assert_duration_limit (clip, 20);
|
||||
|
||||
fail_unless (ges_clip_add_child_to_track (clip, effect0, track0,
|
||||
NULL) == effect0);
|
||||
&error) == effect0);
|
||||
fail_if (error);
|
||||
|
||||
assert_equals_int (limit_notify_count, 2);
|
||||
_assert_duration_limit (clip, 11);
|
||||
|
|
|
@ -404,4 +404,13 @@ G_STMT_START { \
|
|||
free_children_properties (props2, num2); \
|
||||
}
|
||||
|
||||
#define assert_GESError(error, error_code) \
|
||||
{ \
|
||||
fail_unless (error); \
|
||||
fail_unless (error->domain == GES_ERROR); \
|
||||
assert_equals_int (error->code, error_code); \
|
||||
g_error_free (error); \
|
||||
error = NULL; \
|
||||
}
|
||||
|
||||
void print_timeline(GESTimeline *timeline);
|
||||
|
|
|
@ -149,6 +149,21 @@ class GESTest(unittest.TestCase):
|
|||
for effect in effects]
|
||||
self.assertEqual(indexes, list(range(len(effects))))
|
||||
|
||||
def assertGESError(self, error, code, message=""):
|
||||
if error is None:
|
||||
raise AssertionError(
|
||||
"{}{}Received no error".format(message, message and ": "))
|
||||
if error.domain != "GES_ERROR":
|
||||
raise AssertionError(
|
||||
"{}{}Received error ({}) in domain {} rather than "
|
||||
"GES_ERROR".format(
|
||||
message, message and ": ", error.message, error.domain))
|
||||
err_code = GES.Error(error.code)
|
||||
if err_code != code:
|
||||
raise AssertionError(
|
||||
"{}{}Received {} error ({}) rather than {}".format(
|
||||
message, message and ": ", err_code.value_name,
|
||||
error.message, code.value_name))
|
||||
|
||||
class GESSimpleTimelineTest(GESTest):
|
||||
|
||||
|
@ -680,7 +695,7 @@ class GESTimelineConfigTest(GESTest):
|
|||
def assertEdit(self, element, layer, mode, edge, position, snap,
|
||||
snap_froms, snap_tos, new_props, new_transitions,
|
||||
lost_transitions):
|
||||
if not element.edit([], layer, mode, edge, position):
|
||||
if not element.edit_full(layer, mode, edge, position):
|
||||
raise AssertionError(
|
||||
"Edit of {} to layer {}, mode {}, edge {}, at position {} "
|
||||
"failed when a success was expected".format(
|
||||
|
@ -690,11 +705,31 @@ class GESTimelineConfigTest(GESTest):
|
|||
snap_tos=snap_tos, new_transitions=new_transitions,
|
||||
lost_transitions=lost_transitions)
|
||||
|
||||
def assertFailEdit(self, element, layer, mode, edge, position):
|
||||
if element.edit([], layer, mode, edge, position):
|
||||
raise AssertionError(
|
||||
"Edit of {} to layer {}, mode {}, edge {}, at position {} "
|
||||
"succeeded when a failure was expected".format(
|
||||
element, layer, mode, edge, position))
|
||||
def assertFailEdit(self, element, layer, mode, edge, position, err_code):
|
||||
res = None
|
||||
error = None
|
||||
try:
|
||||
res = element.edit_full(layer, mode, edge, position)
|
||||
except GLib.Error as exception:
|
||||
error = exception
|
||||
|
||||
if err_code is None:
|
||||
if res is not False:
|
||||
raise AssertionError(
|
||||
"Edit of {} to layer {}, mode {}, edge {}, at "
|
||||
"position {} succeeded when a failure was expected"
|
||||
"".format(
|
||||
element, layer, mode, edge, position))
|
||||
if error is not None:
|
||||
raise AssertionError(
|
||||
"Edit of {} to layer {}, mode {}, edge {}, at "
|
||||
"position {} did produced an error when none was "
|
||||
"expected".format(
|
||||
element, layer, mode, edge, position))
|
||||
else:
|
||||
self.assertGESError(
|
||||
error, err_code,
|
||||
"Edit of {} to layer {}, mode {}, edge {}, at "
|
||||
"position {}".format(element, layer, mode, edge, position))
|
||||
# should be no change or snapping if edit fails
|
||||
self.assertTimelineConfig()
|
||||
|
|
|
@ -27,6 +27,7 @@ gi.require_version("GES", "1.0")
|
|||
|
||||
from gi.repository import Gst # noqa
|
||||
from gi.repository import GES # noqa
|
||||
from gi.repository import GLib # noqa
|
||||
import unittest # noqa
|
||||
from unittest import mock
|
||||
|
||||
|
@ -561,8 +562,12 @@ class TestEditing(common.GESSimpleTimelineTest):
|
|||
self.assertTrue(clip.set_duration(10))
|
||||
|
||||
# cannot trim to a 0 because effect0 would have a negative in-point
|
||||
self.assertFalse(
|
||||
clip.edit([], -1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 0))
|
||||
error = None
|
||||
try:
|
||||
clip.edit_full(-1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 0)
|
||||
except GLib.Error as err:
|
||||
error = err
|
||||
self.assertGESError(error, GES.Error.NEGATIVE_TIME)
|
||||
|
||||
self.assertEqual(clip.start, 10)
|
||||
self.assertEqual(clip.inpoint, 12)
|
||||
|
@ -945,11 +950,20 @@ class TestInvalidOverlaps(common.GESSimpleTimelineTest):
|
|||
clip2 = self.add_clip(start=10, in_point=0, duration=4)
|
||||
clip3 = self.add_clip(start=12, in_point=0, duration=3)
|
||||
|
||||
self.assertIsNone(clip1.split(13))
|
||||
self.assertIsNone(clip1.split(8))
|
||||
self.assertIsNone(clip1.split_full(13))
|
||||
self.assertIsNone(clip1.split_full(8))
|
||||
self.assertIsNone(clip3.split_full(12))
|
||||
self.assertIsNone(clip3.split_full(15))
|
||||
|
||||
self.assertIsNone(clip3.split(12))
|
||||
self.assertIsNone(clip3.split(15))
|
||||
def _fail_split(self, clip, position):
|
||||
split = None
|
||||
error = None
|
||||
try:
|
||||
split = clip.split_full(position)
|
||||
except GLib.Error as err:
|
||||
error = err
|
||||
self.assertGESError(error, GES.Error.INVALID_OVERLAP_IN_TRACK)
|
||||
self.assertIsNone(split)
|
||||
|
||||
def test_split_with_transition(self):
|
||||
self.track_types = [GES.TrackType.AUDIO]
|
||||
|
@ -958,22 +972,41 @@ class TestInvalidOverlaps(common.GESSimpleTimelineTest):
|
|||
|
||||
clip0 = self.add_clip(start=0, in_point=0, duration=50)
|
||||
clip1 = self.add_clip(start=20, in_point=0, duration=50)
|
||||
clip2 = self.add_clip(start=60, in_point=0, duration=20)
|
||||
self.assertTimelineTopology([
|
||||
[
|
||||
(GES.TestClip, 0, 50),
|
||||
(GES.TransitionClip, 20, 30),
|
||||
(GES.TestClip, 20, 50)
|
||||
(GES.TestClip, 20, 50),
|
||||
(GES.TransitionClip, 60, 10),
|
||||
(GES.TestClip, 60, 20),
|
||||
]
|
||||
])
|
||||
|
||||
# Split should file as the first part of the split
|
||||
# Split should fail as the first part of the split
|
||||
# would be fully overlapping clip0
|
||||
self.assertIsNone(clip1.split(40))
|
||||
self._fail_split(clip1, 40)
|
||||
|
||||
self.assertTimelineTopology([
|
||||
[
|
||||
(GES.TestClip, 0, 50),
|
||||
(GES.TransitionClip, 20, 30),
|
||||
(GES.TestClip, 20, 50)
|
||||
(GES.TestClip, 20, 50),
|
||||
(GES.TransitionClip, 60, 10),
|
||||
(GES.TestClip, 60, 20),
|
||||
]
|
||||
])
|
||||
|
||||
# same with end of the clip
|
||||
self._fail_split(clip1, 65)
|
||||
|
||||
self.assertTimelineTopology([
|
||||
[
|
||||
(GES.TestClip, 0, 50),
|
||||
(GES.TransitionClip, 20, 30),
|
||||
(GES.TestClip, 20, 50),
|
||||
(GES.TransitionClip, 60, 10),
|
||||
(GES.TestClip, 60, 20),
|
||||
]
|
||||
])
|
||||
|
||||
|
@ -1277,79 +1310,82 @@ class TestInvalidOverlaps(common.GESSimpleTimelineTest):
|
|||
|
||||
class TestConfigurationRules(common.GESSimpleTimelineTest):
|
||||
|
||||
def _try_add_clip(self, start, duration, layer=None):
|
||||
def _try_add_clip(self, start, duration, layer=None, error=None):
|
||||
if layer is None:
|
||||
layer = self.layer
|
||||
asset = GES.Asset.request(GES.TestClip, None)
|
||||
found_err = None
|
||||
clip = None
|
||||
# large inpoint to allow trims
|
||||
return layer.add_asset (asset, start, 1000, duration,
|
||||
GES.TrackType.UNKNOWN)
|
||||
try:
|
||||
clip = layer.add_asset_full(
|
||||
asset, start, 1000, duration, GES.TrackType.UNKNOWN)
|
||||
except GLib.Error as err:
|
||||
found_err = err
|
||||
if error is None:
|
||||
self.assertIsNotNone(clip)
|
||||
else:
|
||||
self.assertIsNone(clip)
|
||||
self.assertGESError(found_err, error)
|
||||
return clip
|
||||
|
||||
def test_full_overlap_add(self):
|
||||
clip1 = self._try_add_clip(50, 50)
|
||||
self.assertIsNotNone(clip1)
|
||||
self.assertIsNone(self._try_add_clip(50, 50))
|
||||
self.assertIsNone(self._try_add_clip(49, 51))
|
||||
self.assertIsNone(self._try_add_clip(51, 49))
|
||||
self._try_add_clip(50, 50, error=GES.Error.INVALID_OVERLAP_IN_TRACK)
|
||||
self._try_add_clip(49, 51, error=GES.Error.INVALID_OVERLAP_IN_TRACK)
|
||||
self._try_add_clip(51, 49, error=GES.Error.INVALID_OVERLAP_IN_TRACK)
|
||||
|
||||
def test_triple_overlap_add(self):
|
||||
clip1 = self._try_add_clip(0, 50)
|
||||
self.assertIsNotNone(clip1)
|
||||
clip2 = self._try_add_clip(40, 50)
|
||||
self.assertIsNotNone(clip2)
|
||||
self.assertIsNone(self._try_add_clip(40, 10))
|
||||
self.assertIsNone(self._try_add_clip(30, 30))
|
||||
self.assertIsNone(self._try_add_clip(1, 88))
|
||||
self._try_add_clip(39, 12, error=GES.Error.INVALID_OVERLAP_IN_TRACK)
|
||||
self._try_add_clip(30, 30, error=GES.Error.INVALID_OVERLAP_IN_TRACK)
|
||||
self._try_add_clip(1, 88, error=GES.Error.INVALID_OVERLAP_IN_TRACK)
|
||||
|
||||
def test_full_overlap_move(self):
|
||||
clip1 = self._try_add_clip(0, 50)
|
||||
self.assertIsNotNone(clip1)
|
||||
clip2 = self._try_add_clip(50, 50)
|
||||
self.assertIsNotNone(clip2)
|
||||
self.assertFalse(clip2.set_start(0))
|
||||
|
||||
def test_triple_overlap_move(self):
|
||||
clip1 = self._try_add_clip(0, 50)
|
||||
self.assertIsNotNone(clip1)
|
||||
clip2 = self._try_add_clip(40, 50)
|
||||
self.assertIsNotNone(clip2)
|
||||
clip3 = self._try_add_clip(100, 60)
|
||||
self.assertIsNotNone(clip3)
|
||||
self.assertFalse(clip3.set_start(30))
|
||||
|
||||
def test_full_overlap_move_into_layer(self):
|
||||
clip1 = self._try_add_clip(0, 50)
|
||||
self.assertIsNotNone(clip1)
|
||||
layer2 = self.timeline.append_layer()
|
||||
clip2 = self._try_add_clip(0, 50, layer2)
|
||||
self.assertIsNotNone(clip2)
|
||||
self.assertFalse(clip2.move_to_layer(self.layer))
|
||||
res = None
|
||||
try:
|
||||
res = clip2.move_to_layer_full(self.layer)
|
||||
except GLib.Error as error:
|
||||
self.assertGESError(error, GES.Error.INVALID_OVERLAP_IN_TRACK)
|
||||
self.assertIsNone(res)
|
||||
|
||||
def test_triple_overlap_move_into_layer(self):
|
||||
clip1 = self._try_add_clip(0, 50)
|
||||
self.assertIsNotNone(clip1)
|
||||
clip2 = self._try_add_clip(40, 50)
|
||||
self.assertIsNotNone(clip2)
|
||||
layer2 = self.timeline.append_layer()
|
||||
clip3 = self._try_add_clip(30, 30, layer2)
|
||||
self.assertIsNotNone(clip3)
|
||||
self.assertFalse(clip3.move_to_layer(self.layer))
|
||||
res = None
|
||||
try:
|
||||
res = clip3.move_to_layer_full(self.layer)
|
||||
except GLib.Error as error:
|
||||
self.assertGESError(error, GES.Error.INVALID_OVERLAP_IN_TRACK)
|
||||
self.assertIsNone(res)
|
||||
|
||||
def test_full_overlap_trim(self):
|
||||
clip1 = self._try_add_clip(0, 50)
|
||||
self.assertIsNotNone(clip1)
|
||||
clip2 = self._try_add_clip(50, 50)
|
||||
self.assertIsNotNone(clip2)
|
||||
self.assertFalse(clip2.trim(0))
|
||||
self.assertFalse(clip1.set_duration(100))
|
||||
|
||||
def test_triple_overlap_trim(self):
|
||||
clip1 = self._try_add_clip(0, 20)
|
||||
self.assertIsNotNone(clip1)
|
||||
clip2 = self._try_add_clip(10, 30)
|
||||
self.assertIsNotNone(clip2)
|
||||
clip3 = self._try_add_clip(30, 20)
|
||||
self.assertIsNotNone(clip3)
|
||||
self.assertFalse(clip3.trim(19))
|
||||
self.assertFalse(clip1.set_duration(31))
|
||||
|
||||
|
@ -1506,26 +1542,34 @@ class TestComplexEditing(common.GESTimelineConfigTest):
|
|||
# cannot move c0 up one layer because it would cause a triple
|
||||
# overlap between c1, c2 and c3 when g0 moves
|
||||
self.assertFailEdit(
|
||||
c0, 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 23)
|
||||
c0, 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 23,
|
||||
GES.Error.INVALID_OVERLAP_IN_TRACK)
|
||||
|
||||
# cannot move c0, without moving g1, to 21 layer 1 because it
|
||||
# would be completely overlapped by c2
|
||||
self.assertFailEdit(
|
||||
c0, 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_START, 20)
|
||||
c0, 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_START, 20,
|
||||
GES.Error.INVALID_OVERLAP_IN_TRACK)
|
||||
|
||||
# cannot move c1, without moving g1, with end 25 because it
|
||||
# would be completely overlapped by c2
|
||||
self.assertFailEdit(
|
||||
c0, 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_END, 25)
|
||||
c0, 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_END, 25,
|
||||
GES.Error.INVALID_OVERLAP_IN_TRACK)
|
||||
|
||||
# cannot move g0 to layer 0 because it would make c0 go to a
|
||||
# negative layer
|
||||
self.assertFailEdit(
|
||||
g0, 0, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 10)
|
||||
g0, 0, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 10,
|
||||
GES.Error.NEGATIVE_LAYER)
|
||||
|
||||
# cannot move c1 for same reason
|
||||
self.assertFalse(
|
||||
c1.move_to_layer(self.timeline.get_layer(0)))
|
||||
error = None
|
||||
try:
|
||||
c1.move_to_layer_full(self.timeline.get_layer(0))
|
||||
except GLib.Error as err:
|
||||
error = err
|
||||
self.assertGESError(error, GES.Error.NEGATIVE_LAYER)
|
||||
self.assertTimelineConfig({}, [])
|
||||
|
||||
# failure with snapping
|
||||
|
@ -1534,17 +1578,20 @@ class TestComplexEditing(common.GESTimelineConfigTest):
|
|||
# cannot move to 0 because end edge of c0 would snap with end of
|
||||
# c3, making the new start become negative
|
||||
self.assertFailEdit(
|
||||
g0, 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 0)
|
||||
g0, 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 0,
|
||||
GES.Error.NEGATIVE_TIME)
|
||||
|
||||
# cannot move start of c1 to 14 because snapping causes a full
|
||||
# overlap with c0
|
||||
self.assertFailEdit(
|
||||
c1, 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_START, 14)
|
||||
c1, 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_START, 14,
|
||||
GES.Error.INVALID_OVERLAP_IN_TRACK)
|
||||
|
||||
# cannot move end of c2 to 21 because snapping causes a full
|
||||
# overlap with c0
|
||||
self.assertFailEdit(
|
||||
c2, 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_END, 21)
|
||||
c2, 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_END, 21,
|
||||
GES.Error.INVALID_OVERLAP_IN_TRACK)
|
||||
|
||||
# successes
|
||||
self.timeline.set_snapping_distance(3)
|
||||
|
@ -1701,19 +1748,22 @@ class TestComplexEditing(common.GESTimelineConfigTest):
|
|||
|
||||
# would cause negative layer priority for c0
|
||||
self.assertFailEdit(
|
||||
c1, 0, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_NONE, 5)
|
||||
c1, 0, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_NONE, 5,
|
||||
GES.Error.NEGATIVE_LAYER)
|
||||
|
||||
# would lead to c2 fully overlapping c3 since c2 does ripple
|
||||
# but c3 does not(c3 shares a toplevel with c0, and
|
||||
# GES_EDGE_START, same as NORMAL mode, does not move the
|
||||
# toplevel
|
||||
self.assertFailEdit(
|
||||
c2, 1, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_END, 25)
|
||||
c2, 1, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_END, 25,
|
||||
GES.Error.INVALID_OVERLAP_IN_TRACK)
|
||||
|
||||
# would lead to c2 fully overlapping c3 since c2 does not
|
||||
# ripple but c3 does
|
||||
self.assertFailEdit(
|
||||
c0, 0, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_START, 13)
|
||||
c0, 0, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_START, 13,
|
||||
GES.Error.INVALID_OVERLAP_IN_TRACK)
|
||||
|
||||
# add two more clips
|
||||
|
||||
|
@ -2160,34 +2210,41 @@ class TestComplexEditing(common.GESTimelineConfigTest):
|
|||
# cannot trim end of g0 to 16 because a0 and a1 would fully
|
||||
# overlap
|
||||
self.assertFailEdit(
|
||||
g0, 1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_END, 15)
|
||||
g0, 1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_END, 15,
|
||||
GES.Error.INVALID_OVERLAP_IN_TRACK)
|
||||
|
||||
# cannot edit to new layer because there would be triple overlaps
|
||||
# between v2, v3, v4 and v5
|
||||
self.assertFailEdit(
|
||||
g2, 1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_END, 20)
|
||||
g2, 1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_END, 20,
|
||||
GES.Error.INVALID_OVERLAP_IN_TRACK)
|
||||
|
||||
# cannot trim g1 end to 14 because it would result in a negative
|
||||
# duration for a2 and a4
|
||||
self.assertFailEdit(
|
||||
g1, 1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_END, 14)
|
||||
g1, 1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_END, 14,
|
||||
GES.Error.NEGATIVE_TIME)
|
||||
|
||||
# cannot trim end of v2 below its start
|
||||
self.assertFailEdit(
|
||||
v2, 2, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_END, 2)
|
||||
v2, 2, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_END, 2,
|
||||
GES.Error.NEGATIVE_TIME)
|
||||
|
||||
# cannot trim end of g0 because a0's duration-limit would be
|
||||
# exceeded
|
||||
self.assertFailEdit(
|
||||
g0, 0, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_END, 23)
|
||||
g0, 0, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_END, 23,
|
||||
GES.Error.NOT_ENOUGH_INTERNAL_CONTENT)
|
||||
|
||||
# cannot trim g0 to 12 because a0 and a1 would fully overlap
|
||||
self.assertFailEdit(
|
||||
g0, 0, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 12)
|
||||
g0, 0, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 12,
|
||||
GES.Error.INVALID_OVERLAP_IN_TRACK)
|
||||
|
||||
# cannot trim start of v2 beyond its end point
|
||||
self.assertFailEdit(
|
||||
v2, 2, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 20)
|
||||
v2, 2, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 20,
|
||||
GES.Error.NEGATIVE_TIME)
|
||||
|
||||
# with snapping
|
||||
self.timeline.set_snapping_distance(4)
|
||||
|
@ -2195,12 +2252,14 @@ class TestComplexEditing(common.GESTimelineConfigTest):
|
|||
# cannot trim end of g2 to 19 because v1 and v2 would fully
|
||||
# overlap after snapping to v5 start edge(18)
|
||||
self.assertFailEdit(
|
||||
g2, 0, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_END, 19)
|
||||
g2, 0, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_END, 19,
|
||||
GES.Error.INVALID_OVERLAP_IN_TRACK)
|
||||
|
||||
# cannot trim g2 to 3 because it would snap to start edge of
|
||||
# v4(0), causing v2's in-point to be negative
|
||||
self.assertFailEdit(
|
||||
g2, 0, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 3)
|
||||
g2, 0, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 3,
|
||||
GES.Error.NEGATIVE_TIME)
|
||||
|
||||
# success
|
||||
|
||||
|
@ -2737,46 +2796,66 @@ class TestComplexEditing(common.GESTimelineConfigTest):
|
|||
# cannot roll c10 to 22, which snaps to 23, because it will
|
||||
# extend c5 beyond its duration limit of 8
|
||||
self.assertFailEdit(
|
||||
c10, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_START, 22)
|
||||
c10, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_START, 22,
|
||||
GES.Error.NOT_ENOUGH_INTERNAL_CONTENT)
|
||||
|
||||
# same with g2
|
||||
self.assertFailEdit(
|
||||
g2, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_START, 22)
|
||||
g2, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_START, 22,
|
||||
GES.Error.NOT_ENOUGH_INTERNAL_CONTENT)
|
||||
|
||||
# cannot roll end c9 to 8, which snaps to 7, because it would
|
||||
# cause c3's in-point to become negative
|
||||
self.assertFailEdit(
|
||||
c9, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_END, 8)
|
||||
c9, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_END, 8,
|
||||
GES.Error.NEGATIVE_TIME)
|
||||
|
||||
# same with g1
|
||||
self.assertFailEdit(
|
||||
g1, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_END, 8)
|
||||
g1, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_END, 8,
|
||||
GES.Error.NEGATIVE_TIME)
|
||||
|
||||
# cannot roll c13 to 19, snap to 20, because it would cause
|
||||
# c4 to fully overlap c5
|
||||
self.assertFailEdit(
|
||||
c13, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_START, 19)
|
||||
c13, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_START, 19,
|
||||
GES.Error.INVALID_OVERLAP_IN_TRACK)
|
||||
|
||||
# cannot roll c12 to 11, snap to 10, because it would cause
|
||||
# c3 to fully overlap c4
|
||||
self.assertFailEdit(
|
||||
c12, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_END, 11)
|
||||
c12, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_END, 11,
|
||||
GES.Error.INVALID_OVERLAP_IN_TRACK)
|
||||
|
||||
# give c6 a bit more allowed duration so we can focus on c9
|
||||
self.assertTrue(c6.set_inpoint(10))
|
||||
self.assertTimelineConfig({ c6 : {"in-point": 10}})
|
||||
# cannot roll c6 to 0 because it would cause c9 to be trimmed
|
||||
# below its start
|
||||
self.assertFailEdit(
|
||||
c6, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_START, 0)
|
||||
c6, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_START, 0,
|
||||
GES.Error.NEGATIVE_TIME)
|
||||
# set back
|
||||
self.assertTrue(c6.set_inpoint(7))
|
||||
self.assertTimelineConfig({ c6 : {"in-point": 7}})
|
||||
|
||||
# give c7 a bit more allowed duration so we can focus on c10
|
||||
self.assertTrue(c7.set_inpoint(0))
|
||||
self.assertTimelineConfig({ c7 : {"in-point": 0}})
|
||||
# cannot roll end c7 to 30 because it would cause c10 to be
|
||||
# trimmed beyond its end
|
||||
self.assertFailEdit(
|
||||
c7, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_END, 30)
|
||||
c7, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_END, 30,
|
||||
GES.Error.NEGATIVE_TIME)
|
||||
# set back
|
||||
self.assertTrue(c7.set_inpoint(1))
|
||||
self.assertTimelineConfig({ c7 : {"in-point": 1}})
|
||||
|
||||
# moving layer is not supported
|
||||
self.assertFailEdit(
|
||||
c0, 2, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_START, 7)
|
||||
c0, 2, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_START, 7, None)
|
||||
self.assertFailEdit(
|
||||
c0, 2, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_END, 23)
|
||||
c0, 2, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_END, 23, None)
|
||||
|
||||
# successes
|
||||
self.timeline.set_snapping_distance(0)
|
||||
|
|
Loading…
Reference in a new issue