From 4b62749336c1f5be3326c77f47d04a37fe22cc37 Mon Sep 17 00:00:00 2001 From: Henry Wilkes Date: Mon, 13 Apr 2020 17:42:22 +0100 Subject: [PATCH] clip: add the duration-limit property The duration-limit is the maximum duration that can be set for the clip given its current children and their properties. If a change in the children properties causes this to drop below the current duration, it is automatically capped by this limit. Part-of: --- ges/ges-clip.c | 452 ++++++++++++++++++++++++++------ ges/ges-clip.h | 3 + tests/check/ges/clip.c | 485 ++++++++++++++++++++++++++++------- tests/check/ges/test-utils.h | 20 ++ 4 files changed, 799 insertions(+), 161 deletions(-) diff --git a/ges/ges-clip.c b/ges/ges-clip.c index a97286e389..1b36a2e21d 100644 --- a/ges/ges-clip.c +++ b/ges/ges-clip.c @@ -118,6 +118,9 @@ struct _GESClipPrivate /* The formats supported by this Clip */ GESTrackType supportedformats; + + GstClockTime duration_limit; + gboolean prevent_duration_limit_update; }; enum @@ -125,6 +128,7 @@ enum PROP_0, PROP_LAYER, PROP_SUPPORTED_FORMATS, + PROP_DURATION_LIMIT, PROP_LAST }; @@ -148,11 +152,176 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GESClip, ges_clip, #define _IS_CORE_CHILD(child) GES_TRACK_ELEMENT_IS_CORE(child) #define _IS_TOP_EFFECT(child) \ - (!_IS_CORE_CHILD (child) && GES_IS_BASE_EFFECT (child)) + (!_IS_CORE_CHILD (child) && GES_IS_BASE_EFFECT (child)) #define _IS_CORE_INTERNAL_SOURCE_CHILD(child) \ (_IS_CORE_CHILD (child) \ - && ges_track_element_has_internal_source (GES_TRACK_ELEMENT (child))) + && ges_track_element_has_internal_source (GES_TRACK_ELEMENT (child))) + +#define _MIN_CLOCK_TIME(a, b) \ + (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)) + + +typedef struct _DurationLimitData +{ + GESTrackElement *child; + GESTrack *track; + guint32 priority; + GstClockTime max_duration; + GstClockTime inpoint; + gboolean active; +} DurationLimitData; + +static DurationLimitData * +_duration_limit_data_new (GESTrackElement * child) +{ + GESTrack *track = ges_track_element_get_track (child); + DurationLimitData *data = g_new0 (DurationLimitData, 1); + + data->child = gst_object_ref (child); + data->track = track ? gst_object_ref (track) : NULL; + data->inpoint = _INPOINT (child); + data->max_duration = _MAXDURATION (child); + data->priority = _PRIORITY (child); + data->active = ges_track_element_is_active (child); + + return data; +} + +static void +_duration_limit_data_free (gpointer data_p) +{ + DurationLimitData *data = data_p; + gst_clear_object (&data->track); + gst_clear_object (&data->child); + g_free (data); +} + +static GList * +_duration_limit_data_list (GESClip * clip) +{ + GList *tmp, *list = NULL; + + for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next) + list = g_list_prepend (list, _duration_limit_data_new (tmp->data)); + + return list; +} + +static gint +_cmp_by_track_then_priority (gconstpointer a_p, gconstpointer b_p) +{ + const DurationLimitData *a = a_p, *b = b_p; + if (a->track < b->track) + return -1; + else if (a->track > b->track) + return 1; + /* if higher priority (numerically lower) place later */ + if (a->priority < b->priority) + return -1; + else if (a->priority > b->priority) + return 1; + return 0; +} + +#define _INTERNAL_LIMIT(data) \ + ((data->active && GST_CLOCK_TIME_IS_VALID (data->max_duration)) ? \ + data->max_duration - data->inpoint : GST_CLOCK_TIME_NONE) + +static GstClockTime +_calculate_duration_limit (GESClip * self, GList * child_data) +{ + GstClockTime limit = GST_CLOCK_TIME_NONE; + GList *tmp; + + child_data = g_list_sort (child_data, _cmp_by_track_then_priority); + + tmp = child_data; + + while (tmp) { + /* we have the first element in the track, of the lowest priority, and + * work our way up from here */ + DurationLimitData *data = tmp->data; + GESTrack *track = data->track; + if (track) { + GstClockTime track_limit = _INTERNAL_LIMIT (data); + + for (tmp = tmp->next; tmp; tmp = tmp->next) { + data = tmp->data; + if (data->track != track) + break; + track_limit = _MIN_CLOCK_TIME (track_limit, _INTERNAL_LIMIT (data)); + } + + GST_LOG_OBJECT (self, "duration-limit for track %" GST_PTR_FORMAT + " is %" GST_TIME_FORMAT, track, GST_TIME_ARGS (track_limit)); + limit = _MIN_CLOCK_TIME (limit, track_limit); + } else { + /* children not in a track do not affect the duration-limit */ + for (tmp = tmp->next; tmp; tmp = tmp->next) { + data = tmp->data; + if (data->track) + break; + } + } + } + GST_LOG_OBJECT (self, "calculated duration-limit for the clip is %" + GST_TIME_FORMAT, GST_TIME_ARGS (limit)); + + g_list_free_full (child_data, _duration_limit_data_free); + + return limit; +} + +static void +_update_duration_limit (GESClip * self) +{ + GstClockTime duration_limit; + + if (self->priv->prevent_duration_limit_update) + return; + + duration_limit = _calculate_duration_limit (self, + _duration_limit_data_list (self)); + + if (duration_limit != self->priv->duration_limit) { + GESTimelineElement *element = GES_TIMELINE_ELEMENT (self); + + self->priv->duration_limit = duration_limit; + 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)) { + gboolean res; + + GST_INFO_OBJECT (self, "Automatically reducing duration to %" + GST_TIME_FORMAT " to match the new duration-limit because " + "the current duration %" GST_TIME_FORMAT " exceeds it", + GST_TIME_ARGS (duration_limit), GST_TIME_ARGS (element->duration)); + + /* trim end with no snapping */ + 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); + else + res = ges_timeline_element_set_duration (element, duration_limit); + + if (!res) + GST_ERROR_OBJECT (self, "Could not reduce the duration of the " + "clip to below its duration-limit of %" GST_TIME_FORMAT, + GST_TIME_ARGS (duration_limit)); + } + /* notify after the auto-change in duration to allow the user to set + * the duration in response to the change in their callbacks */ + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DURATION_LIMIT]); + } +} /* @min_priority: The absolute minimum priority a child of @container should have * @max_priority: The absolute maximum priority a child of @container should have @@ -181,8 +350,7 @@ _get_priority_range (GESContainer * container, guint32 * min_priority, } static void -_child_priority_changed_cb (GESTimelineElement * child, - GParamSpec * arg G_GNUC_UNUSED, GESContainer * container) +_child_priority_changed (GESContainer * container, GESTimelineElement * child) { /* we do not change the rest of the clip in response to a change in * the child priority */ @@ -206,24 +374,29 @@ _child_priority_changed_cb (GESTimelineElement * child, } } -static void -_child_inpoint_changed_cb (GESTimelineElement * child, GParamSpec * pspec, - GESContainer * container) +/* returns TRUE if duration-limit needs to be updated */ +static gboolean +_child_inpoint_changed (GESClip * self, GESTimelineElement * child) { - if (GES_CLIP (container)->priv->setting_inpoint) - return; + if (self->priv->setting_inpoint) + return FALSE; - /* ignore non-core */ - /* if the track element has no internal content, then this means its - * in-point has been set (back) to 0, we can ignore this update */ + /* if we have a non-core child, then we do not need the in-point of the + * clip to change. Similarly, if the track element is core but has no + * internal content, then this means its in-point has been set (back) to + * 0, which means we do not need to update the in-point of the clip. */ if (!_IS_CORE_INTERNAL_SOURCE_CHILD (child)) - return; + return TRUE; + + /* if setting the in-point of the clip, this will handle the change in + * the duration-limit */ /* If the child->inpoint is the same as our own, set_inpoint will do * nothing. For example, when we set them in add_child (the notifies for * this are released after child_added is called because * ges_container_add freezes them) */ - _set_inpoint0 (GES_TIMELINE_ELEMENT (container), child->inpoint); + _set_inpoint0 (GES_TIMELINE_ELEMENT (self), child->inpoint); + return FALSE; } /* called when a child is added, removed or their max-duration changes */ @@ -239,10 +412,8 @@ _update_max_duration (GESContainer * container) for (tmp = container->children; tmp; tmp = tmp->next) { GESTimelineElement *child = tmp->data; - if (_IS_CORE_CHILD (child) - && GST_CLOCK_TIME_IS_VALID (child->maxduration)) - min = GST_CLOCK_TIME_IS_VALID (min) ? MIN (min, child->maxduration) : - child->maxduration; + if (_IS_CORE_CHILD (child)) + min = _MIN_CLOCK_TIME (min, child->maxduration); } priv->updating_max_duration = TRUE; ges_timeline_element_set_max_duration (GES_TIMELINE_ELEMENT (container), min); @@ -250,8 +421,8 @@ _update_max_duration (GESContainer * container) } static void -_child_max_duration_changed_cb (GESTimelineElement * child, - GParamSpec * pspec, GESContainer * container) +_child_max_duration_changed (GESContainer * container, + GESTimelineElement * child) { /* ignore non-core */ if (!_IS_CORE_CHILD (child)) @@ -261,8 +432,7 @@ _child_max_duration_changed_cb (GESTimelineElement * child, } static void -_child_has_internal_source_changed_cb (GESTimelineElement * child, - GParamSpec * pspec, GESContainer * container) +_child_has_internal_source_changed (GESClip * self, GESTimelineElement * child) { /* ignore non-core */ /* if the track element is now registered to have no internal content, @@ -271,7 +441,34 @@ _child_has_internal_source_changed_cb (GESTimelineElement * child, return; /* otherwise, we need to make its in-point match ours */ - _set_inpoint0 (child, _INPOINT (container)); + _set_inpoint0 (child, _INPOINT (self)); +} + +#define _IS_PROP(prop) (g_strcmp0 (name, prop) == 0) + +static void +_child_property_changed_cb (GESTimelineElement * child, GParamSpec * pspec, + GESClip * self) +{ + gboolean update = FALSE; + const gchar *name = pspec->name; + + if (_IS_PROP ("track") || _IS_PROP ("active")) { + update = TRUE; + } else if (_IS_PROP ("priority")) { + update = TRUE; + _child_priority_changed (GES_CONTAINER (self), child); + } else if (_IS_PROP ("in-point")) { + update = _child_inpoint_changed (self, child); + } else if (_IS_PROP ("max-duration")) { + update = TRUE; + _child_max_duration_changed (GES_CONTAINER (self), child); + } else if (_IS_PROP ("has-internal-source")) { + _child_has_internal_source_changed (self, child); + } + + if (update) + _update_duration_limit (self); } /**************************************************** @@ -416,10 +613,13 @@ static gboolean _set_childrens_inpoint (GESTimelineElement * element, GstClockTime inpoint, gboolean break_on_failure) { + GESClip *self = GES_CLIP (element); GList *tmp; - GESClipPrivate *priv = GES_CLIP (element)->priv; + GESClipPrivate *priv = self->priv; + gboolean prev_prevent = priv->prevent_duration_limit_update; priv->setting_inpoint = TRUE; + priv->prevent_duration_limit_update = TRUE; for (tmp = GES_CONTAINER_CHILDREN (element); tmp; tmp = tmp->next) { GESTimelineElement *child = tmp->data; @@ -428,12 +628,18 @@ _set_childrens_inpoint (GESTimelineElement * element, GstClockTime inpoint, GST_ERROR_OBJECT ("Could not set the in-point of child %" GES_FORMAT " to %" GST_TIME_FORMAT, GES_ARGS (child), GST_TIME_ARGS (inpoint)); - if (break_on_failure) + if (break_on_failure) { + priv->setting_inpoint = FALSE; + priv->prevent_duration_limit_update = prev_prevent; return FALSE; + } } } } priv->setting_inpoint = FALSE; + priv->prevent_duration_limit_update = prev_prevent; + + _update_duration_limit (self); return TRUE; } @@ -452,7 +658,6 @@ static gboolean _set_duration (GESTimelineElement * element, GstClockTime duration) { GList *tmp, *children; - GESContainer *container = GES_CONTAINER (element); /* get copy of children, since GESContainer may resort the clip */ @@ -477,9 +682,12 @@ static gboolean _set_max_duration (GESTimelineElement * element, GstClockTime maxduration) { GList *tmp; - GESClipPrivate *priv = GES_CLIP (element)->priv; + GESClip *self = GES_CLIP (element); + GESClipPrivate *priv = self->priv; GstClockTime new_min = GST_CLOCK_TIME_NONE; gboolean has_core = FALSE; + gboolean res = FALSE; + gboolean prev_prevent = priv->prevent_duration_limit_update; /* if we are setting based on a change in the minimum */ if (priv->updating_max_duration) @@ -487,6 +695,7 @@ _set_max_duration (GESTimelineElement * element, GstClockTime maxduration) /* else, we set every core child to have the same max duration */ + priv->prevent_duration_limit_update = TRUE; priv->prevent_max_duration_update = TRUE; for (tmp = GES_CONTAINER_CHILDREN (element); tmp; tmp = tmp->next) { GESTimelineElement *child = tmp->data; @@ -498,14 +707,12 @@ _set_max_duration (GESTimelineElement * element, GstClockTime maxduration) GST_ERROR_OBJECT ("Could not set the max-duration of child %" GES_FORMAT " to %" GST_TIME_FORMAT, GES_ARGS (child), GST_TIME_ARGS (maxduration)); - - if (GST_CLOCK_TIME_IS_VALID (child->maxduration)) - new_min = GST_CLOCK_TIME_IS_VALID (new_min) ? - MIN (new_min, child->maxduration) : child->maxduration; + new_min = _MIN_CLOCK_TIME (new_min, child->maxduration); } } } priv->prevent_max_duration_update = FALSE; + priv->prevent_duration_limit_update = prev_prevent; if (!has_core) { /* allow max-duration to be set arbitrarily when we have no @@ -515,7 +722,8 @@ _set_max_duration (GESTimelineElement * element, GstClockTime maxduration) GST_INFO_OBJECT (element, "Allowing max-duration of the clip to be set to %" GST_TIME_FORMAT " because it has no core children", GST_TIME_ARGS (maxduration)); - return TRUE; + res = TRUE; + goto done; } if (new_min != maxduration) { @@ -532,10 +740,15 @@ _set_max_duration (GESTimelineElement * element, GstClockTime maxduration) priv->updating_max_duration = TRUE; ges_timeline_element_set_max_duration (element, new_min); priv->updating_max_duration = FALSE; - return FALSE; + goto done; } - return TRUE; + res = TRUE; + +done: + _update_duration_limit (self); + + return res; } static gboolean @@ -544,7 +757,7 @@ _set_priority (GESTimelineElement * element, guint32 priority) GESClipPrivate *priv = GES_CLIP (element)->priv; GList *tmp; guint32 min_prio, max_prio; - + gboolean prev_prevent = priv->prevent_duration_limit_update; GESContainer *container = GES_CONTAINER (element); /* send the new 'priority' to determine what the new 'min_prio' should @@ -554,6 +767,7 @@ _set_priority (GESTimelineElement * element, guint32 priority) /* offsets will remain constant for the children */ priv->prevent_resort = TRUE; priv->prevent_priority_offset_update = TRUE; + priv->prevent_duration_limit_update = TRUE; for (tmp = container->children; tmp; tmp = g_list_next (tmp)) { guint32 track_element_prio; GESTimelineElement *child = (GESTimelineElement *) tmp->data; @@ -582,9 +796,11 @@ _set_priority (GESTimelineElement * element, guint32 priority) _set_priority0 (child, track_element_prio); } /* no need to re-sort the container since we maintained the relative - * offsets. As such, the height remains the same as well. */ + * offsets. As such, the height and duration-limit remains the same as + * well. */ priv->prevent_resort = FALSE; priv->prevent_priority_offset_update = FALSE; + priv->prevent_duration_limit_update = prev_prevent; return TRUE; } @@ -650,28 +866,30 @@ _compute_height (GESContainer * container) static gboolean _add_child (GESContainer * container, GESTimelineElement * element) { + GESClip *self = GES_CLIP (container); GESClipClass *klass = GES_CLIP_GET_CLASS (GES_CLIP (container)); guint max_prio, min_prio; GESTrack *track; GESTimeline *timeline = GES_TIMELINE_ELEMENT_TIMELINE (container); - GESClipPrivate *priv = GES_CLIP (container)->priv; + GESClipPrivate *priv = self->priv; GESAsset *asset, *creator_asset; + gboolean prev_prevent = priv->prevent_duration_limit_update; g_return_val_if_fail (GES_IS_TRACK_ELEMENT (element), FALSE); if (element->timeline && element->timeline != timeline) { - GST_WARNING_OBJECT (container, "Cannot add %" GES_FORMAT " as a child " + GST_WARNING_OBJECT (self, "Cannot add %" GES_FORMAT " as a child " "because its timeline is %" GST_PTR_FORMAT " rather than the " "clip's timeline %" GST_PTR_FORMAT, GES_ARGS (element), element->timeline, timeline); return FALSE; } - asset = ges_extractable_get_asset (GES_EXTRACTABLE (container)); + asset = ges_extractable_get_asset (GES_EXTRACTABLE (self)); creator_asset = ges_track_element_get_creator_asset (GES_TRACK_ELEMENT (element)); if (creator_asset && asset != creator_asset) { - GST_WARNING_OBJECT (container, + GST_WARNING_OBJECT (self, "Cannot add the track element %" GES_FORMAT " as a child " "because it is a core element created by another clip with a " "different asset to the current clip's asset", GES_ARGS (element)); @@ -685,7 +903,7 @@ _add_child (GESContainer * container, GESTimelineElement * element) * the track, so we would have checked this with the * element->timeline check. But technically a user could get around * this, so we double check here. */ - GST_WARNING_OBJECT (container, "Cannot add %" GES_FORMAT " as a child " + GST_WARNING_OBJECT (self, "Cannot add %" GES_FORMAT " as a child " "because its track %" GST_PTR_FORMAT " is part of the timeline %" GST_PTR_FORMAT " rather than the clip's timeline %" GST_PTR_FORMAT, GES_ARGS (element), track, ges_track_get_timeline (track), timeline); @@ -700,8 +918,8 @@ _add_child (GESContainer * container, GESTimelineElement * element) * list of added effects, so we do not increase nb_effects. */ if (track && !priv->allow_any_track - && _track_contains_core (GES_CLIP (container), track, TRUE)) { - GST_WARNING_OBJECT (container, "Cannot add the core child %" GES_FORMAT + && _track_contains_core (self, track, TRUE)) { + GST_WARNING_OBJECT (self, "Cannot add the core child %" GES_FORMAT " because it is in the same track %" GST_PTR_FORMAT " as an " "existing core child", GES_ARGS (element), track); return FALSE; @@ -712,11 +930,10 @@ _add_child (GESContainer * container, GESTimelineElement * element) if (ges_track_element_has_internal_source (GES_TRACK_ELEMENT (element))) { /* adding can fail if the max-duration of the element is smaller * than the current in-point of the clip */ - if (!_set_inpoint0 (element, _INPOINT (container))) { + if (!_set_inpoint0 (element, _INPOINT (self))) { GST_ERROR_OBJECT (element, "Could not set the in-point of the " "element %" GES_FORMAT " to %" GST_TIME_FORMAT ". Not adding " - "as a child", GES_ARGS (element), - GST_TIME_ARGS (_INPOINT (container))); + "as a child", GES_ARGS (element), GST_TIME_ARGS (_INPOINT (self))); return FALSE; } } @@ -730,20 +947,21 @@ _add_child (GESContainer * container, GESTimelineElement * element) * to make room. */ if (track && !priv->allow_any_track - && !_track_contains_core (GES_CLIP (container), track, TRUE)) { - GST_WARNING_OBJECT (container, "Cannot add the effect %" GES_FORMAT + && !_track_contains_core (GES_CLIP (self), track, TRUE)) { + GST_WARNING_OBJECT (self, "Cannot add the effect %" GES_FORMAT " because its track %" GST_PTR_FORMAT " does not contain one " "of the clip's core children", GES_ARGS (element), track); return FALSE; } - GST_DEBUG_OBJECT (container, "Adding %ith effect: %" GES_FORMAT + GST_DEBUG_OBJECT (self, "Adding %ith effect: %" GES_FORMAT " Priority %i", priv->nb_effects + 1, GES_ARGS (element), min_prio + priv->nb_effects); /* changing priorities, and updating their offset */ priv->prevent_resort = TRUE; - tmp = g_list_nth (GES_CONTAINER_CHILDREN (container), priv->nb_effects); + priv->prevent_duration_limit_update = TRUE; + tmp = g_list_nth (container->children, priv->nb_effects); for (; tmp; tmp = tmp->next) ges_timeline_element_set_priority (GES_TIMELINE_ELEMENT (tmp->data), GES_TIMELINE_ELEMENT_PRIORITY (tmp->data) + 1); @@ -751,30 +969,32 @@ _add_child (GESContainer * container, GESTimelineElement * element) _set_priority0 (element, min_prio + priv->nb_effects); priv->nb_effects++; priv->prevent_resort = FALSE; + priv->prevent_duration_limit_update = prev_prevent; /* no need to call _ges_container_sort_children (container) since * there is no change to the ordering yet (this happens after the * child is actually added) */ /* The height has already changed (increased by 1) */ _compute_height (container); + /* update duration limit in _child_added */ } else { if (_IS_TOP_EFFECT (element)) - GST_WARNING_OBJECT (container, "Cannot add the effect %" GES_FORMAT + GST_WARNING_OBJECT (self, "Cannot add the effect %" GES_FORMAT " because it is not a core element created by the clip itself " "and the %s class does not allow for adding extra effects", GES_ARGS (element), G_OBJECT_CLASS_NAME (klass)); else if (GES_CLIP_CLASS_CAN_ADD_EFFECTS (klass)) - GST_WARNING_OBJECT (container, "Cannot add the track element %" + GST_WARNING_OBJECT (self, "Cannot add the track element %" GES_FORMAT " because it is neither a core element created by " "the clip itself, nor a GESBaseEffect", GES_ARGS (element)); else - GST_WARNING_OBJECT (container, "Cannot add the track element %" + GST_WARNING_OBJECT (self, "Cannot add the track element %" GES_FORMAT " because it is not a core element created by the " "clip itself", GES_ARGS (element)); return FALSE; } - _set_start0 (element, GES_TIMELINE_ELEMENT_START (container)); - _set_duration0 (element, GES_TIMELINE_ELEMENT_DURATION (container)); + _set_start0 (element, GES_TIMELINE_ELEMENT_START (self)); + _set_duration0 (element, GES_TIMELINE_ELEMENT_DURATION (self)); return TRUE; } @@ -787,10 +1007,12 @@ _remove_child (GESContainer * container, GESTimelineElement * element) /* NOTE: notifies are currently frozen by ges_container_add */ if (_IS_TOP_EFFECT (element)) { GList *tmp; + gboolean prev_prevent = priv->prevent_duration_limit_update; GST_DEBUG_OBJECT (container, "Resyncing effects priority."); /* changing priorities, so preventing a re-sort */ priv->prevent_resort = TRUE; + priv->prevent_duration_limit_update = TRUE; for (tmp = GES_CONTAINER_CHILDREN (container); tmp; tmp = tmp->next) { guint32 sibling_prio = GES_TIMELINE_ELEMENT_PRIORITY (tmp->data); if (sibling_prio > element->priority) @@ -799,46 +1021,44 @@ _remove_child (GESContainer * container, GESTimelineElement * element) } priv->nb_effects--; priv->prevent_resort = FALSE; + priv->prevent_duration_limit_update = prev_prevent; /* no need to re-sort the children since the rest keep the same * relative priorities */ /* height may have changed */ _compute_height (container); } + /* duration-limit updated in _child_removed */ return TRUE; } static void _child_added (GESContainer * container, GESTimelineElement * element) { - g_signal_connect (element, "notify::priority", - G_CALLBACK (_child_priority_changed_cb), container); - g_signal_connect (element, "notify::in-point", - G_CALLBACK (_child_inpoint_changed_cb), container); - g_signal_connect (element, "notify::max-duration", - G_CALLBACK (_child_max_duration_changed_cb), container); - g_signal_connect (element, "notify::has-internal-source", - G_CALLBACK (_child_has_internal_source_changed_cb), container); + GESClip *self = GES_CLIP (container); - _child_priority_changed_cb (element, NULL, container); + g_signal_connect (element, "notify", G_CALLBACK (_child_property_changed_cb), + self); + + _child_priority_changed (container, element); if (_IS_CORE_CHILD (element)) _update_max_duration (container); + + _update_duration_limit (self); } static void _child_removed (GESContainer * container, GESTimelineElement * element) { - g_signal_handlers_disconnect_by_func (element, _child_priority_changed_cb, - container); - g_signal_handlers_disconnect_by_func (element, _child_inpoint_changed_cb, - container); - g_signal_handlers_disconnect_by_func (element, - _child_max_duration_changed_cb, container); - g_signal_handlers_disconnect_by_func (element, - _child_has_internal_source_changed_cb, container); + GESClip *self = GES_CLIP (container); + + g_signal_handlers_disconnect_by_func (element, _child_property_changed_cb, + self); if (_IS_CORE_CHILD (element)) _update_max_duration (container); + + _update_duration_limit (self); } static void @@ -855,12 +1075,17 @@ add_clip_to_list (gpointer key, gpointer clip, GList ** list) * NOTE: Since this does not change the creator asset of the child, this * should only be called for transferring children between clips with the * same asset. + * NOTE: This also prevents the update of the duration-limit, so you + * should ensure that you call _update_duration_limit on both clips when + * transferring has completed. */ static void _transfer_child (GESClip * from_clip, GESClip * to_clip, GESTrackElement * child) { GESTimeline *timeline = GES_TIMELINE_ELEMENT_TIMELINE (to_clip); + gboolean prev_prevent_from = from_clip->priv->prevent_duration_limit_update; + gboolean prev_prevent_to = to_clip->priv->prevent_duration_limit_update; /* We need to bump the refcount to avoid the object to be destroyed */ gst_object_ref (child); @@ -868,6 +1093,9 @@ _transfer_child (GESClip * from_clip, GESClip * to_clip, /* don't want to change tracks */ ges_timeline_set_moving_track_elements (timeline, TRUE); + from_clip->priv->prevent_duration_limit_update = TRUE; + to_clip->priv->prevent_duration_limit_update = TRUE; + ges_container_remove (GES_CONTAINER (from_clip), GES_TIMELINE_ELEMENT (child)); @@ -879,6 +1107,9 @@ _transfer_child (GESClip * from_clip, GESClip * to_clip, to_clip->priv->allow_any_track = FALSE; ges_timeline_set_moving_track_elements (timeline, FALSE); + from_clip->priv->prevent_duration_limit_update = prev_prevent_from; + to_clip->priv->prevent_duration_limit_update = prev_prevent_to; + gst_object_unref (child); } @@ -942,6 +1173,14 @@ _ungroup (GESContainer * container, gboolean recursive) g_hash_table_foreach (_tracktype_clip, (GHFunc) add_clip_to_list, &ret); g_hash_table_unref (_tracktype_clip); + /* Need to update the duration limit. + * Since we have divided the clip by its tracks, the duration-limit, + * which is a minimum value calculated per track, can only increase in + * value, which means the duration of the clip should not change, which + * means updating should always be possible */ + for (tmp = ret; tmp; tmp = tmp->next) + _update_duration_limit (tmp->data); + return ret; } @@ -1062,10 +1301,31 @@ _group (GList * containers) supported_formats |= ges_track_element_get_track_type (celement); } g_list_free_full (children, gst_object_unref); + /* duration-limit should be GST_CLOCK_TIME_NONE now that we have no + * children */ + _update_duration_limit (cclip); ges_layer_remove_clip (layer, cclip); } + /* Need to update the duration limit. + * Each received clip C_i that has been grouped may have had a different + * duration-limit L_i. In each case the duration must be less than + * this limit, and since each clip shares the same duration, we have + * for each clip C_i: + * duration <= L_i + * Thus: + * duration <= min_i (L_i) + * + * Now, upon grouping each clip C_i into C, we have not changed the + * children properties that affect the duration-limit. And since the + * duration-limit is calculated as the minimum amongst the tracks of C, + * this means that the duration-limit for C should be + * L = min_i (L_i) >= duration + * Therefore, we can safely set the duration-limit of C to L without + * changing the duration of C. */ + _update_duration_limit (GES_CLIP (ret)); + ges_clip_set_supported_formats (GES_CLIP (ret), supported_formats); return ret; @@ -1075,10 +1335,13 @@ void ges_clip_empty_from_track (GESClip * clip, GESTrack * track) { GList *tmp; + gboolean prev_prevent = clip->priv->prevent_duration_limit_update; + if (track == NULL) return; /* allow us to remove in any order */ clip->priv->allow_any_track = TRUE; + clip->priv->prevent_duration_limit_update = TRUE; for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next) { GESTrackElement *child = tmp->data; @@ -1089,6 +1352,8 @@ ges_clip_empty_from_track (GESClip * clip, GESTrack * track) } } clip->priv->allow_any_track = FALSE; + clip->priv->prevent_duration_limit_update = prev_prevent; + _update_duration_limit (clip); } static GESTrackElement * @@ -1261,6 +1526,8 @@ ges_clip_get_property (GObject * object, guint property_id, case PROP_SUPPORTED_FORMATS: g_value_set_flags (value, clip->priv->supportedformats); break; + case PROP_DURATION_LIMIT: + g_value_set_uint64 (value, clip->priv->duration_limit); default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } @@ -1338,6 +1605,30 @@ ges_clip_class_init (GESClipClass * klass) g_object_class_install_property (object_class, PROP_LAYER, properties[PROP_LAYER]); + /** + * GESClip:duration-limit: + * + * The maximum #GESTimelineElement:duration that can be *currently* set + * for the clip, taking into account the #GESTimelineElement:in-point, + * #GESTimelineElement:max-duration, GESTrackElement:active, and + * #GESTrackElement:track properties of its children. If there is no + * limit, this will be set to #GST_CLOCK_TIME_NONE. + * + * Note that whilst a clip has no children in any tracks, the limit will + * be unknown, and similarly set to #GST_CLOCK_TIME_NONE. + * + * If the duration-limit would ever go below the current + * #GESTimelineElement:duration of the clip due to a change in the above + * variables, its #GESTimelineElement:duration will be set to the new + * limit. + */ + properties[PROP_DURATION_LIMIT] = + g_param_spec_uint64 ("duration-limit", "Duration Limit", + "A limit on the duration of the clip", 0, G_MAXUINT64, + GST_CLOCK_TIME_NONE, G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY); + g_object_class_install_property (object_class, PROP_DURATION_LIMIT, + properties[PROP_DURATION_LIMIT]); + element_class->ripple = _ripple; element_class->ripple_end = _ripple_end; element_class->roll_start = _roll_start; @@ -1367,6 +1658,7 @@ static void ges_clip_init (GESClip * self) { self->priv = ges_clip_get_instance_private (self); + self->priv->duration_limit = GST_CLOCK_TIME_NONE; } /** @@ -1697,6 +1989,22 @@ ges_clip_get_layer (GESClip * clip) return clip->priv->layer; } +/** + * ges_clip_get_duration_limit: + * @clip: A #GESClip + * + * Gets the #GESClip:duration-limit of the clip. + * + * Returns: The duration-limit of @clip. + */ +GstClockTime +ges_clip_get_duration_limit (GESClip * clip) +{ + g_return_val_if_fail (GES_IS_CLIP (clip), GST_CLOCK_TIME_NONE); + + return clip->priv->duration_limit; +} + /** * ges_clip_get_top_effects: * @clip: A #GESClip diff --git a/ges/ges-clip.h b/ges/ges-clip.h index 628d756fa9..1e2c5d9a07 100644 --- a/ges/ges-clip.h +++ b/ges/ges-clip.h @@ -193,4 +193,7 @@ GstClockTime ges_clip_get_timeline_time_from_source_frame (GESClip * clip, GESFrameNumber frame_number, GError ** err); +GES_API +GstClockTime ges_clip_get_duration_limit (GESClip * clip); + G_END_DECLS diff --git a/tests/check/ges/clip.c b/tests/check/ges/clip.c index 2c26ea58e9..08e78bac02 100644 --- a/tests/check/ges/clip.c +++ b/tests/check/ges/clip.c @@ -22,6 +22,14 @@ #include #include +#define _assert_add(clip, child) \ + fail_unless (ges_container_add (GES_CONTAINER (clip), \ + GES_TIMELINE_ELEMENT (child))) + +#define _assert_remove(clip, child) \ + fail_unless (ges_container_remove (GES_CONTAINER (clip), \ + GES_TIMELINE_ELEMENT (child))) + GST_START_TEST (test_object_properties) { GESClip *clip; @@ -95,8 +103,7 @@ GST_START_TEST (test_object_properties) nle_object_check (ges_track_element_get_nleobject (trackelement), 400, 510, 120, 510, MIN_NLE_PRIO + TRANSITIONS_HEIGHT, TRUE); - ges_container_remove (GES_CONTAINER (clip), - GES_TIMELINE_ELEMENT (trackelement)); + _assert_remove (clip, trackelement); gst_object_unref (timeline); @@ -365,10 +372,10 @@ GST_START_TEST (test_split_object) GES_TIMELINE_ELEMENT (clip)); effect1 = GES_TRACK_ELEMENT (ges_effect_new ("agingtv")); - ges_container_add (GES_CONTAINER (clip), GES_TIMELINE_ELEMENT (effect1)); + _assert_add (clip, effect1); effect2 = GES_TRACK_ELEMENT (ges_effect_new ("vertigotv")); - ges_container_add (GES_CONTAINER (clip), GES_TIMELINE_ELEMENT (effect2)); + _assert_add (clip, effect2); /* Check that trackelement has the same properties */ CHECK_OBJECT_PROPS (trackelement1, 42, 12, 50); @@ -578,18 +585,15 @@ GST_START_TEST (test_clip_group_ungroup) el = GES_TRACK_ELEMENT (ges_effect_new ("audioecho")); ges_track_element_set_track_type (el, GES_TRACK_TYPE_AUDIO); - fail_unless (ges_container_add (GES_CONTAINER (clip), - GES_TIMELINE_ELEMENT (el))); + _assert_add (clip, el); el = GES_TRACK_ELEMENT (ges_effect_new ("agingtv")); ges_track_element_set_track_type (el, GES_TRACK_TYPE_VIDEO); - fail_unless (ges_container_add (GES_CONTAINER (clip), - GES_TIMELINE_ELEMENT (el))); + _assert_add (clip, el); el = GES_TRACK_ELEMENT (ges_effect_new ("videobalance")); ges_track_element_set_track_type (el, GES_TRACK_TYPE_VIDEO); - fail_unless (ges_container_add (GES_CONTAINER (clip), - GES_TIMELINE_ELEMENT (el))); + _assert_add (clip, el); assert_num_children (clip, 5); CHECK_OBJECT_PROPS (clip, 0, 0, 10); @@ -696,27 +700,27 @@ GST_START_TEST (test_clip_group_ungroup) assert_num_in_track (audio_track, 2); assert_num_in_track (video_track, 3); - ges_timeline_element_set_start (GES_TIMELINE_ELEMENT (video_clip), 10); + assert_set_start (video_clip, 10); CHECK_OBJECT_PROPS (video_clip, 10, 0, 10); CHECK_OBJECT_PROPS (audio_clip, 0, 0, 10); _assert_regroup_fails (containers); - ges_timeline_element_set_start (GES_TIMELINE_ELEMENT (video_clip), 0); - ges_timeline_element_set_inpoint (GES_TIMELINE_ELEMENT (video_clip), 10); + assert_set_start (video_clip, 0); + assert_set_inpoint (video_clip, 10); CHECK_OBJECT_PROPS (video_clip, 0, 10, 10); CHECK_OBJECT_PROPS (audio_clip, 0, 0, 10); _assert_regroup_fails (containers); - ges_timeline_element_set_inpoint (GES_TIMELINE_ELEMENT (video_clip), 0); - ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (video_clip), 15); + assert_set_inpoint (video_clip, 0); + assert_set_duration (video_clip, 15); CHECK_OBJECT_PROPS (video_clip, 0, 0, 15); CHECK_OBJECT_PROPS (audio_clip, 0, 0, 10); _assert_regroup_fails (containers); - ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (video_clip), 10); + assert_set_duration (video_clip, 10); CHECK_OBJECT_PROPS (video_clip, 0, 0, 10); CHECK_OBJECT_PROPS (audio_clip, 0, 0, 10); @@ -897,8 +901,7 @@ GST_START_TEST (test_clip_can_group) /* can group if same asset but different tracks */ clip1 = ges_layer_add_asset (layer1, asset2, 0, 0, 10, GES_TRACK_TYPE_VIDEO); fail_unless (clip1); - fail_unless (ges_container_add (GES_CONTAINER (clip1), - GES_TIMELINE_ELEMENT (ges_effect_new ("agingtv")))); + _assert_add (clip1, ges_effect_new ("agingtv")); assert_num_children (clip1, 2); clip2 = ges_layer_add_asset (layer1, asset2, 0, 0, 10, GES_TRACK_TYPE_AUDIO); @@ -997,11 +1000,9 @@ GST_START_TEST (test_adding_children_to_track) fail_unless (ges_track_element_get_track (source) == track1); effect = GES_TRACK_ELEMENT (ges_effect_new ("agingtv")); - fail_unless (ges_container_add (GES_CONTAINER (clip), - GES_TIMELINE_ELEMENT (effect))); + _assert_add (clip, effect); effect2 = GES_TRACK_ELEMENT (ges_effect_new ("vertigotv")); - fail_unless (ges_container_add (GES_CONTAINER (clip), - GES_TIMELINE_ELEMENT (effect2))); + _assert_add (clip, effect2); assert_num_children (clip, 3); assert_num_in_track (track1, 3); assert_num_in_track (track2, 0); @@ -1154,8 +1155,7 @@ GST_START_TEST (test_adding_children_to_track) /* removing core from the container, empties the non-core from their * tracks */ gst_object_ref (added); - fail_unless (ges_container_remove (GES_CONTAINER (clip), - GES_TIMELINE_ELEMENT (added))); + _assert_remove (clip, added); assert_num_children (clip, 5); fail_unless (ges_track_element_get_track (source) == track1); fail_if (ges_track_element_get_track (added)); @@ -1167,10 +1167,8 @@ GST_START_TEST (test_adding_children_to_track) assert_num_in_track (track2, 0); gst_object_unref (added); - fail_unless (ges_container_remove (GES_CONTAINER (clip), - GES_TIMELINE_ELEMENT (added2))); - fail_unless (ges_container_remove (GES_CONTAINER (clip), - GES_TIMELINE_ELEMENT (added3))); + _assert_remove (clip, added2); + _assert_remove (clip, added3); assert_num_children (clip, 3); assert_num_in_track (track1, 3); assert_num_in_track (track2, 0); @@ -1234,14 +1232,13 @@ GST_START_TEST (test_adding_children_to_track) /* can not add source at time 23 because it would result in three * overlapping sources in the track */ - fail_unless (ges_timeline_element_set_start (GES_TIMELINE_ELEMENT (clip), - 23)); + assert_set_start (clip, 23); fail_if (ges_clip_add_child_to_track (clip, source, track1, NULL)); assert_num_children (clip, 3); assert_num_in_track (track1, 4); /* can add at 5, with overlap */ - fail_unless (ges_timeline_element_set_start (GES_TIMELINE_ELEMENT (clip), 5)); + assert_set_start (clip, 5); added = ges_clip_add_child_to_track (clip, source, track1, NULL); /* added is the source since it was not already in a track */ fail_unless (added == source); @@ -1314,8 +1311,7 @@ GST_START_TEST (test_clip_refcount_remove_child) assert_num_in_track (track, 2); ASSERT_OBJECT_REFCOUNT (effect, "1 for the track + 1 timeline", 2); - fail_unless (ges_container_add (GES_CONTAINER (clip), - GES_TIMELINE_ELEMENT (effect))); + _assert_add (clip, effect); assert_num_children (clip, 2); ASSERT_OBJECT_REFCOUNT (effect, "1 for the container + 1 for the track" " + 1 timeline", 3); @@ -1326,8 +1322,7 @@ GST_START_TEST (test_clip_refcount_remove_child) g_signal_connect (clip, "child-removed", G_CALLBACK (child_removed_cb), &called); gst_object_ref (effect); - fail_unless (ges_container_remove (GES_CONTAINER (clip), - GES_TIMELINE_ELEMENT (effect))); + _assert_remove (clip, effect); fail_unless (called == TRUE); ASSERT_OBJECT_REFCOUNT (effect, "1 test ref", 1); gst_object_unref (effect); @@ -1377,18 +1372,15 @@ GST_START_TEST (test_clip_find_track_element) effect = GES_TRACK_ELEMENT (ges_effect_new ("identity")); fail_unless (ges_track_add_element (track, effect)); - fail_unless (ges_container_add (GES_CONTAINER (clip), - GES_TIMELINE_ELEMENT (effect))); + _assert_add (clip, effect); effect1 = GES_TRACK_ELEMENT (ges_effect_new ("identity")); fail_unless (ges_track_add_element (track1, effect1)); - fail_unless (ges_container_add (GES_CONTAINER (clip), - GES_TIMELINE_ELEMENT (effect1))); + _assert_add (clip, effect1); effect2 = GES_TRACK_ELEMENT (ges_effect_new ("identity")); fail_unless (ges_track_add_element (track2, effect2)); - fail_unless (ges_container_add (GES_CONTAINER (clip), - GES_TIMELINE_ELEMENT (effect2))); + _assert_add (clip, effect2); fail_if (selection_called); assert_num_children (clip, 6); @@ -1529,16 +1521,13 @@ GST_START_TEST (test_effects_priorities) ges_layer_add_clip (layer, clip); effect = GES_TRACK_ELEMENT (ges_effect_new ("agingtv")); - fail_unless (ges_container_add (GES_CONTAINER (clip), - GES_TIMELINE_ELEMENT (effect))); + _assert_add (clip, effect); effect1 = GES_TRACK_ELEMENT (ges_effect_new ("agingtv")); - fail_unless (ges_container_add (GES_CONTAINER (clip), - GES_TIMELINE_ELEMENT (effect1))); + _assert_add (clip, effect1); effect2 = GES_TRACK_ELEMENT (ges_effect_new ("agingtv")); - fail_unless (ges_container_add (GES_CONTAINER (clip), - GES_TIMELINE_ELEMENT (effect2))); + _assert_add (clip, effect2); fail_unless_equals_int (MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 0, _PRIORITY (effect)); @@ -1712,10 +1701,10 @@ GST_START_TEST (test_children_time_setters) ges_track_element_set_has_internal_source (child, TRUE); _test_children_time_setting_on_clip (clip, child); /* clip in a group */ - fail_unless (ges_container_add (group, GES_TIMELINE_ELEMENT (clip))); + _assert_add (group, clip); _test_children_time_setting_on_clip (clip, child); /* group is removed from the timeline and destroyed when empty */ - ges_container_remove (group, GES_TIMELINE_ELEMENT (clip)); + _assert_remove (group, clip); /* child not in timeline */ gst_object_ref (clip); fail_unless (ges_layer_remove_clip (layer, clip)); @@ -1803,9 +1792,9 @@ GST_START_TEST (test_children_inpoint) clip = GES_TIMELINE_ELEMENT (ges_test_clip_new ()); - fail_unless (ges_timeline_element_set_start (clip, 5)); - fail_unless (ges_timeline_element_set_duration (clip, 20)); - fail_unless (ges_timeline_element_set_inpoint (clip, 30)); + assert_set_start (clip, 5); + assert_set_duration (clip, 20); + assert_set_inpoint (clip, 30); CHECK_OBJECT_PROPS (clip, 5, 30, 20); @@ -1833,13 +1822,13 @@ GST_START_TEST (test_children_inpoint) fail_if (ges_track_element_has_internal_source (GES_TRACK_ELEMENT (effect))); /* allow us to choose our own in-point */ ges_track_element_set_has_internal_source (GES_TRACK_ELEMENT (effect), TRUE); - fail_unless (ges_timeline_element_set_start (effect, 104)); - fail_unless (ges_timeline_element_set_duration (effect, 53)); - fail_unless (ges_timeline_element_set_inpoint (effect, 67)); + assert_set_start (effect, 104); + assert_set_duration (effect, 53); + assert_set_inpoint (effect, 67); /* adding the effect will change its start and duration, but not its * in-point */ - fail_unless (ges_container_add (GES_CONTAINER (clip), effect)); + _assert_add (clip, effect); CHECK_OBJECT_PROPS (clip, 5, 30, 20); CHECK_OBJECT_PROPS (child0, 5, 30, 20); @@ -1865,7 +1854,7 @@ GST_START_TEST (test_children_inpoint) /* when we set the in-point on a core-child with an internal source we * also set the clip and siblings with the same features */ - fail_unless (ges_timeline_element_set_inpoint (child1, 50)); + assert_set_inpoint (child1, 50); CHECK_OBJECT_PROPS (clip, 5, 50, 20); /* child with no internal source not changed */ @@ -1883,7 +1872,7 @@ GST_START_TEST (test_children_inpoint) CHECK_OBJECT_PROPS (child1, 5, 50, 20); CHECK_OBJECT_PROPS (effect, 5, 67, 20); - fail_unless (ges_timeline_element_set_inpoint (child0, 40)); + assert_set_inpoint (child0, 40); CHECK_OBJECT_PROPS (clip, 5, 40, 20); CHECK_OBJECT_PROPS (child0, 5, 40, 20); @@ -1891,7 +1880,7 @@ GST_START_TEST (test_children_inpoint) CHECK_OBJECT_PROPS (effect, 5, 67, 20); /* setting in-point on effect shouldn't change any other siblings */ - fail_unless (ges_timeline_element_set_inpoint (effect, 77)); + assert_set_inpoint (effect, 77); CHECK_OBJECT_PROPS (clip, 5, 40, 20); CHECK_OBJECT_PROPS (child0, 5, 40, 20); @@ -1943,13 +1932,13 @@ GST_START_TEST (test_children_max_duration) max_duration = clips[i].max_duration; fail_unless_equals_uint64 (_MAX_DURATION (clip), max_duration); - fail_unless (ges_timeline_element_set_start (clip, 5)); - fail_unless (ges_timeline_element_set_duration (clip, 20)); - fail_unless (ges_timeline_element_set_inpoint (clip, 30)); + assert_set_start (clip, 5); + assert_set_duration (clip, 20); + assert_set_inpoint (clip, 30); /* can set the max duration the clip to anything whilst it has * no core child */ - fail_unless (ges_timeline_element_set_max_duration (clip, 150)); + assert_set_max_duration (clip, 150); CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, 150); @@ -1960,31 +1949,31 @@ GST_START_TEST (test_children_max_duration) /* allow us to choose our own max-duration */ ges_track_element_set_has_internal_source (GES_TRACK_ELEMENT (effect), TRUE); - fail_unless (ges_timeline_element_set_start (effect, 104)); - fail_unless (ges_timeline_element_set_duration (effect, 53)); - fail_unless (ges_timeline_element_set_max_duration (effect, 400)); + assert_set_start (effect, 104); + assert_set_duration (effect, 53); + assert_set_max_duration (effect, 400); /* adding the effect will change its start and duration, but not its * max-duration (or in-point) */ - fail_unless (ges_container_add (GES_CONTAINER (clip), effect)); + _assert_add (clip, effect); CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, 150); CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 400); /* only non-core, so can still set the max-duration */ - fail_unless (ges_timeline_element_set_max_duration (clip, 200)); + assert_set_max_duration (clip, 200); CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, 200); CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 400); /* removing should not change the max-duration we set on the clip */ gst_object_ref (effect); - fail_unless (ges_container_remove (GES_CONTAINER (clip), effect)); + _assert_remove (clip, effect); CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, 200); CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 400); - fail_unless (ges_container_add (GES_CONTAINER (clip), effect)); + _assert_add (clip, effect); gst_object_unref (effect); CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, 200); @@ -2022,21 +2011,21 @@ GST_START_TEST (test_children_max_duration) /* when setting max_duration of core children, clip will take the * minimum value */ - fail_unless (ges_timeline_element_set_max_duration (child0, new_max - 1)); + assert_set_max_duration (child0, new_max - 1); CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, new_max - 1); CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, new_max - 1); CHECK_OBJECT_PROPS_MAX (child1, 5, 30, 20, max_duration); CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 400); - fail_unless (ges_timeline_element_set_max_duration (child1, new_max - 2)); + assert_set_max_duration (child1, new_max - 2); CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, new_max - 2); CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, new_max - 1); CHECK_OBJECT_PROPS_MAX (child1, 5, 30, 20, new_max - 2); CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 400); - fail_unless (ges_timeline_element_set_max_duration (child0, new_max + 1)); + assert_set_max_duration (child0, new_max + 1); CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, new_max - 2); CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, new_max + 1); @@ -2092,21 +2081,21 @@ GST_START_TEST (test_children_max_duration) CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 400); /* setting below new_max is ok */ - fail_unless (ges_timeline_element_set_inpoint (child0, 15)); + assert_set_inpoint (child0, 15); CHECK_OBJECT_PROPS_MAX (clip, 5, 15, 20, new_max - 2); CHECK_OBJECT_PROPS_MAX (child0, 5, 15, 20, new_max + 1); CHECK_OBJECT_PROPS_MAX (child1, 5, 15, 20, new_max - 2); CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 400); - fail_unless (ges_timeline_element_set_inpoint (child1, 25)); + assert_set_inpoint (child1, 25); CHECK_OBJECT_PROPS_MAX (clip, 5, 25, 20, new_max - 2); CHECK_OBJECT_PROPS_MAX (child0, 5, 25, 20, new_max + 1); CHECK_OBJECT_PROPS_MAX (child1, 5, 25, 20, new_max - 2); CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 400); - fail_unless (ges_timeline_element_set_inpoint (clip, 30)); + assert_set_inpoint (clip, 30); CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, new_max - 2); CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, new_max + 1); @@ -2114,7 +2103,7 @@ GST_START_TEST (test_children_max_duration) CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 400); /* non-core has no effect */ - fail_unless (ges_timeline_element_set_max_duration (effect, new_max + 500)); + assert_set_max_duration (effect, new_max + 500); CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, new_max - 2); CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, new_max + 1); @@ -2123,7 +2112,7 @@ GST_START_TEST (test_children_max_duration) /* can set the in-point of non-core to be higher than the max_duration * of the clip */ - fail_unless (ges_timeline_element_set_inpoint (effect, new_max + 2)); + assert_set_inpoint (effect, new_max + 2); CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, new_max - 2); CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, new_max + 1); @@ -2145,14 +2134,14 @@ GST_START_TEST (test_children_max_duration) CHECK_OBJECT_PROPS_MAX (child1, 5, 30, 20, new_max - 2); CHECK_OBJECT_PROPS_MAX (effect, 5, new_max + 2, 20, new_max + 500); - fail_unless (ges_timeline_element_set_inpoint (effect, 0)); + assert_set_inpoint (effect, 0); CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, new_max - 2); CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, new_max + 1); CHECK_OBJECT_PROPS_MAX (child1, 5, 30, 20, new_max - 2); CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, new_max + 500); - fail_unless (ges_timeline_element_set_max_duration (effect, 400)); + assert_set_max_duration (effect, 400); CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, new_max - 2); CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, new_max + 1); @@ -2161,7 +2150,7 @@ GST_START_TEST (test_children_max_duration) /* setting on the clip will set all the core children to the same * value */ - fail_unless (ges_timeline_element_set_max_duration (clip, 180)); + assert_set_max_duration (clip, 180); CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, 180); CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, 180); @@ -2217,7 +2206,7 @@ GST_START_TEST (test_children_max_duration) CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 400); /* can now set the max-duration, which will effect the clip */ - fail_unless (ges_timeline_element_set_max_duration (child0, 140)); + assert_set_max_duration (child0, 140); CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, 140); CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, 140); @@ -2232,7 +2221,7 @@ GST_START_TEST (test_children_max_duration) CHECK_OBJECT_PROPS_MAX (child1, 5, 30, 20, GST_CLOCK_TIME_NONE); CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 400); - fail_unless (ges_timeline_element_set_max_duration (child1, 130)); + assert_set_max_duration (child1, 130); CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, 130); CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, 140); @@ -2245,7 +2234,7 @@ GST_START_TEST (test_children_max_duration) gst_object_ref (effect); /* removing non-core does nothing */ - fail_unless (ges_container_remove (GES_CONTAINER (clip), effect)); + _assert_remove (clip, effect); CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, 130); CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, 140); @@ -2253,7 +2242,7 @@ GST_START_TEST (test_children_max_duration) CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 400); /* new minimum max-duration for the clip when we remove child1 */ - fail_unless (ges_container_remove (GES_CONTAINER (clip), child1)); + _assert_remove (clip, child1); CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, 140); CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, 140); @@ -2262,7 +2251,7 @@ GST_START_TEST (test_children_max_duration) /* with no core-children, the max-duration of the clip is set to * GST_CLOCK_TIME_NONE */ - fail_unless (ges_container_remove (GES_CONTAINER (clip), child0)); + _assert_remove (clip, child0); CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, GST_CLOCK_TIME_NONE); CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, 140); @@ -2283,6 +2272,323 @@ GST_START_TEST (test_children_max_duration) GST_END_TEST; +#define _assert_duration_limit(clip, expect) \ + assert_equals_uint64 (ges_clip_get_duration_limit (GES_CLIP (clip)), expect) + +GST_START_TEST (test_duration_limit) +{ + GESTimeline *timeline; + GESLayer *layer; + GESClip *clip; + GESTrackElement *video_source, *audio_source; + GESTrackElement *effect1, *effect2, *effect3; + GESTrack *track1, *track2; + gint limit_notify_count = 0; + gint duration_notify_count = 0; + + ges_init (); + + timeline = ges_timeline_new (); + track1 = GES_TRACK (ges_video_track_new ()); + track2 = GES_TRACK (ges_audio_track_new ()); + + fail_unless (ges_timeline_add_track (timeline, track1)); + fail_unless (ges_timeline_add_track (timeline, track2)); + /* add track3 later */ + + layer = ges_timeline_append_layer (timeline); + + clip = GES_CLIP (ges_test_clip_new ()); + g_signal_connect (clip, "notify::duration-limit", G_CALLBACK (_count_cb), + &limit_notify_count); + g_signal_connect (clip, "notify::duration", G_CALLBACK (_count_cb), + &duration_notify_count); + + /* no limit to begin with */ + _assert_duration_limit (clip, GST_CLOCK_TIME_NONE); + + /* add effects */ + effect1 = GES_TRACK_ELEMENT (ges_effect_new ("textoverlay")); + ges_track_element_set_has_internal_source (effect1, TRUE); + + effect2 = GES_TRACK_ELEMENT (ges_effect_new ("agingtv")); + ges_track_element_set_has_internal_source (effect2, TRUE); + + effect3 = GES_TRACK_ELEMENT (ges_effect_new ("audioecho")); + ges_track_element_set_has_internal_source (effect3, TRUE); + + _assert_add (clip, effect1); + _assert_add (clip, effect2); + _assert_add (clip, effect3); + assert_num_children (clip, 3); + _assert_duration_limit (clip, GST_CLOCK_TIME_NONE); + assert_equals_int (limit_notify_count, 0); + assert_equals_int (duration_notify_count, 0); + + /* no change in duration limit whilst children are not in any track */ + assert_set_max_duration (effect1, 20); + _assert_duration_limit (clip, GST_CLOCK_TIME_NONE); + assert_equals_int (limit_notify_count, 0); + assert_equals_int (duration_notify_count, 0); + + assert_set_inpoint (effect1, 5); + _assert_duration_limit (clip, GST_CLOCK_TIME_NONE); + assert_equals_int (limit_notify_count, 0); + assert_equals_int (duration_notify_count, 0); + + /* set a duration that will be above the duration-limit */ + assert_set_duration (clip, 20); + assert_equals_int (duration_notify_count, 1); + + /* add to layer to create sources */ + fail_unless (ges_layer_add_clip (layer, clip)); + + /* duration-limit changes once because of effect1 */ + _assert_duration_limit (clip, 15); + assert_equals_int (limit_notify_count, 1); + assert_equals_int (duration_notify_count, 2); + /* duration has automatically been set to the duration-limit */ + CHECK_OBJECT_PROPS_MAX (clip, 0, 0, 15, GST_CLOCK_TIME_NONE); + + assert_num_children (clip, 5); + assert_num_in_track (track1, 3); + assert_num_in_track (track2, 2); + + video_source = ges_clip_find_track_element (clip, track1, GES_TYPE_SOURCE); + fail_unless (video_source); + gst_object_unref (video_source); + + audio_source = ges_clip_find_track_element (clip, track2, GES_TYPE_SOURCE); + fail_unless (audio_source); + gst_object_unref (audio_source); + + CHECK_OBJECT_PROPS_MAX (video_source, 0, 0, 15, GST_CLOCK_TIME_NONE); + fail_unless (ges_track_element_get_track (video_source) == track1); + fail_unless (ges_track_element_get_track_type (video_source) == + GES_TRACK_TYPE_VIDEO); + CHECK_OBJECT_PROPS_MAX (audio_source, 0, 0, 15, GST_CLOCK_TIME_NONE); + fail_unless (ges_track_element_get_track (audio_source) == track2); + fail_unless (ges_track_element_get_track_type (audio_source) == + GES_TRACK_TYPE_AUDIO); + CHECK_OBJECT_PROPS_MAX (effect1, 0, 5, 15, 20); + fail_unless (ges_track_element_get_track (effect1) == track1); + CHECK_OBJECT_PROPS_MAX (effect2, 0, 0, 15, GST_CLOCK_TIME_NONE); + fail_unless (ges_track_element_get_track (effect2) == track1); + CHECK_OBJECT_PROPS_MAX (effect3, 0, 0, 15, GST_CLOCK_TIME_NONE); + fail_unless (ges_track_element_get_track (effect3) == track2); + + /* Make effect1 inactive, which will remove the duration-limit */ + fail_unless (ges_track_element_set_active (effect1, FALSE)); + _assert_duration_limit (clip, GST_CLOCK_TIME_NONE); + assert_equals_int (limit_notify_count, 2); + /* duration is unchanged */ + assert_equals_int (duration_notify_count, 2); + CHECK_OBJECT_PROPS_MAX (clip, 0, 0, 15, GST_CLOCK_TIME_NONE); + CHECK_OBJECT_PROPS_MAX (video_source, 0, 0, 15, GST_CLOCK_TIME_NONE); + CHECK_OBJECT_PROPS_MAX (audio_source, 0, 0, 15, GST_CLOCK_TIME_NONE); + CHECK_OBJECT_PROPS_MAX (effect1, 0, 5, 15, 20); + CHECK_OBJECT_PROPS_MAX (effect2, 0, 0, 15, GST_CLOCK_TIME_NONE); + CHECK_OBJECT_PROPS_MAX (effect3, 0, 0, 15, GST_CLOCK_TIME_NONE); + + /* changing the in-point does not change the duration limit whilst + * there is no max-duration */ + assert_set_inpoint (clip, 10); + _assert_duration_limit (clip, GST_CLOCK_TIME_NONE); + assert_equals_int (limit_notify_count, 2); + assert_equals_int (duration_notify_count, 2); + CHECK_OBJECT_PROPS_MAX (clip, 0, 10, 15, GST_CLOCK_TIME_NONE); + CHECK_OBJECT_PROPS_MAX (video_source, 0, 10, 15, GST_CLOCK_TIME_NONE); + CHECK_OBJECT_PROPS_MAX (audio_source, 0, 10, 15, GST_CLOCK_TIME_NONE); + CHECK_OBJECT_PROPS_MAX (effect1, 0, 5, 15, 20); + CHECK_OBJECT_PROPS_MAX (effect2, 0, 0, 15, GST_CLOCK_TIME_NONE); + CHECK_OBJECT_PROPS_MAX (effect3, 0, 0, 15, GST_CLOCK_TIME_NONE); + + assert_set_max_duration (video_source, 40); + _assert_duration_limit (clip, 30); + assert_equals_int (limit_notify_count, 3); + assert_equals_int (duration_notify_count, 2); + CHECK_OBJECT_PROPS_MAX (clip, 0, 10, 15, 40); + CHECK_OBJECT_PROPS_MAX (video_source, 0, 10, 15, 40); + CHECK_OBJECT_PROPS_MAX (audio_source, 0, 10, 15, GST_CLOCK_TIME_NONE); + CHECK_OBJECT_PROPS_MAX (effect1, 0, 5, 15, 20); + CHECK_OBJECT_PROPS_MAX (effect2, 0, 0, 15, GST_CLOCK_TIME_NONE); + CHECK_OBJECT_PROPS_MAX (effect3, 0, 0, 15, GST_CLOCK_TIME_NONE); + + /* set in-point using child */ + assert_set_inpoint (audio_source, 15); + _assert_duration_limit (clip, 25); + assert_equals_int (limit_notify_count, 4); + assert_equals_int (duration_notify_count, 2); + CHECK_OBJECT_PROPS_MAX (clip, 0, 15, 15, 40); + CHECK_OBJECT_PROPS_MAX (video_source, 0, 15, 15, 40); + CHECK_OBJECT_PROPS_MAX (audio_source, 0, 15, 15, GST_CLOCK_TIME_NONE); + CHECK_OBJECT_PROPS_MAX (effect1, 0, 5, 15, 20); + CHECK_OBJECT_PROPS_MAX (effect2, 0, 0, 15, GST_CLOCK_TIME_NONE); + CHECK_OBJECT_PROPS_MAX (effect3, 0, 0, 15, GST_CLOCK_TIME_NONE); + + /* set max-duration of core children */ + assert_set_max_duration (clip, 60); + _assert_duration_limit (clip, 45); + assert_equals_int (limit_notify_count, 5); + assert_equals_int (duration_notify_count, 2); + CHECK_OBJECT_PROPS_MAX (clip, 0, 15, 15, 60); + CHECK_OBJECT_PROPS_MAX (video_source, 0, 15, 15, 60); + CHECK_OBJECT_PROPS_MAX (audio_source, 0, 15, 15, 60); + CHECK_OBJECT_PROPS_MAX (effect1, 0, 5, 15, 20); + CHECK_OBJECT_PROPS_MAX (effect2, 0, 0, 15, GST_CLOCK_TIME_NONE); + CHECK_OBJECT_PROPS_MAX (effect3, 0, 0, 15, GST_CLOCK_TIME_NONE); + + /* can set duration up to the limit */ + assert_set_duration (clip, 45); + _assert_duration_limit (clip, 45); + assert_equals_int (limit_notify_count, 5); + assert_equals_int (duration_notify_count, 3); + CHECK_OBJECT_PROPS_MAX (clip, 0, 15, 45, 60); + CHECK_OBJECT_PROPS_MAX (video_source, 0, 15, 45, 60); + CHECK_OBJECT_PROPS_MAX (audio_source, 0, 15, 45, 60); + /* effect1 has a duration that exceeds max-duration - in-point + * ok since it is currently inactive */ + CHECK_OBJECT_PROPS_MAX (effect1, 0, 5, 45, 20); + CHECK_OBJECT_PROPS_MAX (effect2, 0, 0, 45, GST_CLOCK_TIME_NONE); + CHECK_OBJECT_PROPS_MAX (effect3, 0, 0, 45, GST_CLOCK_TIME_NONE); + + /* limit the effects */ + assert_set_max_duration (effect2, 70); + _assert_duration_limit (clip, 45); + assert_equals_int (limit_notify_count, 5); + assert_equals_int (duration_notify_count, 3); + CHECK_OBJECT_PROPS_MAX (clip, 0, 15, 45, 60); + CHECK_OBJECT_PROPS_MAX (video_source, 0, 15, 45, 60); + CHECK_OBJECT_PROPS_MAX (audio_source, 0, 15, 45, 60); + CHECK_OBJECT_PROPS_MAX (effect1, 0, 5, 45, 20); + CHECK_OBJECT_PROPS_MAX (effect2, 0, 0, 45, 70); + CHECK_OBJECT_PROPS_MAX (effect3, 0, 0, 45, GST_CLOCK_TIME_NONE); + + assert_set_inpoint (effect2, 40); + _assert_duration_limit (clip, 30); + assert_equals_int (limit_notify_count, 6); + assert_equals_int (duration_notify_count, 4); + CHECK_OBJECT_PROPS_MAX (clip, 0, 15, 30, 60); + CHECK_OBJECT_PROPS_MAX (video_source, 0, 15, 30, 60); + CHECK_OBJECT_PROPS_MAX (audio_source, 0, 15, 30, 60); + CHECK_OBJECT_PROPS_MAX (effect1, 0, 5, 30, 20); + CHECK_OBJECT_PROPS_MAX (effect2, 0, 40, 30, 70); + CHECK_OBJECT_PROPS_MAX (effect3, 0, 0, 30, GST_CLOCK_TIME_NONE); + + /* no change */ + assert_set_max_duration (effect3, 35); + _assert_duration_limit (clip, 30); + assert_equals_int (limit_notify_count, 6); + assert_equals_int (duration_notify_count, 4); + CHECK_OBJECT_PROPS_MAX (clip, 0, 15, 30, 60); + CHECK_OBJECT_PROPS_MAX (video_source, 0, 15, 30, 60); + CHECK_OBJECT_PROPS_MAX (audio_source, 0, 15, 30, 60); + CHECK_OBJECT_PROPS_MAX (effect1, 0, 5, 30, 20); + CHECK_OBJECT_PROPS_MAX (effect2, 0, 40, 30, 70); + CHECK_OBJECT_PROPS_MAX (effect3, 0, 0, 30, 35); + + /* make effect1 active again */ + fail_unless (ges_track_element_set_active (effect1, TRUE)); + _assert_duration_limit (clip, 15); + assert_equals_int (limit_notify_count, 7); + assert_equals_int (duration_notify_count, 5); + CHECK_OBJECT_PROPS_MAX (clip, 0, 15, 15, 60); + CHECK_OBJECT_PROPS_MAX (video_source, 0, 15, 15, 60); + CHECK_OBJECT_PROPS_MAX (audio_source, 0, 15, 15, 60); + CHECK_OBJECT_PROPS_MAX (effect1, 0, 5, 15, 20); + CHECK_OBJECT_PROPS_MAX (effect2, 0, 40, 15, 70); + CHECK_OBJECT_PROPS_MAX (effect3, 0, 0, 15, 35); + + /* removing effect2 from track does not change limit */ + fail_unless (ges_track_remove_element (track1, effect2)); + _assert_duration_limit (clip, 15); + assert_equals_int (limit_notify_count, 7); + assert_equals_int (duration_notify_count, 5); + CHECK_OBJECT_PROPS_MAX (clip, 0, 15, 15, 60); + CHECK_OBJECT_PROPS_MAX (video_source, 0, 15, 15, 60); + CHECK_OBJECT_PROPS_MAX (audio_source, 0, 15, 15, 60); + CHECK_OBJECT_PROPS_MAX (effect1, 0, 5, 15, 20); + CHECK_OBJECT_PROPS_MAX (effect3, 0, 0, 15, 35); + /* no track */ + CHECK_OBJECT_PROPS_MAX (effect2, 0, 40, 15, 70); + + /* removing effect1 does */ + fail_unless (ges_track_remove_element (track1, effect1)); + _assert_duration_limit (clip, 35); + assert_equals_int (limit_notify_count, 8); + assert_equals_int (duration_notify_count, 5); + CHECK_OBJECT_PROPS_MAX (clip, 0, 15, 15, 60); + CHECK_OBJECT_PROPS_MAX (video_source, 0, 15, 15, 60); + CHECK_OBJECT_PROPS_MAX (audio_source, 0, 15, 15, 60); + CHECK_OBJECT_PROPS_MAX (effect3, 0, 0, 15, 35); + /* no track */ + CHECK_OBJECT_PROPS_MAX (effect1, 0, 5, 15, 20); + CHECK_OBJECT_PROPS_MAX (effect2, 0, 40, 15, 70); + + /* add back */ + fail_unless (ges_track_add_element (track1, effect2)); + _assert_duration_limit (clip, 30); + assert_equals_int (limit_notify_count, 9); + assert_equals_int (duration_notify_count, 5); + CHECK_OBJECT_PROPS_MAX (clip, 0, 15, 15, 60); + CHECK_OBJECT_PROPS_MAX (audio_source, 0, 15, 15, 60); + CHECK_OBJECT_PROPS_MAX (effect3, 0, 0, 15, 35); + CHECK_OBJECT_PROPS_MAX (video_source, 0, 15, 15, 60); + CHECK_OBJECT_PROPS_MAX (effect2, 0, 40, 15, 70); + /* no track */ + CHECK_OBJECT_PROPS_MAX (effect1, 0, 5, 15, 20); + + assert_set_duration (clip, 20); + _assert_duration_limit (clip, 30); + assert_equals_int (limit_notify_count, 9); + assert_equals_int (duration_notify_count, 6); + CHECK_OBJECT_PROPS_MAX (clip, 0, 15, 20, 60); + CHECK_OBJECT_PROPS_MAX (audio_source, 0, 15, 20, 60); + CHECK_OBJECT_PROPS_MAX (effect3, 0, 0, 20, 35); + CHECK_OBJECT_PROPS_MAX (video_source, 0, 15, 20, 60); + CHECK_OBJECT_PROPS_MAX (effect2, 0, 40, 20, 70); + /* no track */ + CHECK_OBJECT_PROPS_MAX (effect1, 0, 5, 20, 20); + + fail_unless (ges_track_add_element (track1, effect1)); + _assert_duration_limit (clip, 15); + assert_equals_int (limit_notify_count, 10); + assert_equals_int (duration_notify_count, 7); + CHECK_OBJECT_PROPS_MAX (clip, 0, 15, 15, 60); + CHECK_OBJECT_PROPS_MAX (audio_source, 0, 15, 15, 60); + CHECK_OBJECT_PROPS_MAX (effect3, 0, 0, 15, 35); + CHECK_OBJECT_PROPS_MAX (video_source, 0, 15, 15, 60); + CHECK_OBJECT_PROPS_MAX (effect1, 0, 5, 15, 20); + CHECK_OBJECT_PROPS_MAX (effect2, 0, 40, 15, 70); + + gst_object_ref (clip); + fail_unless (ges_layer_remove_clip (layer, clip)); + + assert_num_in_track (track1, 0); + assert_num_in_track (track2, 0); + assert_num_children (clip, 5); + + _assert_duration_limit (clip, GST_CLOCK_TIME_NONE); + /* may have several changes in duration limit as the children are + * emptied from their tracks */ + fail_unless (limit_notify_count > 10); + assert_equals_int (duration_notify_count, 7); + /* none in any track */ + CHECK_OBJECT_PROPS_MAX (clip, 0, 15, 15, 60); + CHECK_OBJECT_PROPS_MAX (audio_source, 0, 15, 15, 60); + CHECK_OBJECT_PROPS_MAX (effect3, 0, 0, 15, 35); + CHECK_OBJECT_PROPS_MAX (video_source, 0, 15, 15, 60); + CHECK_OBJECT_PROPS_MAX (effect1, 0, 5, 15, 20); + CHECK_OBJECT_PROPS_MAX (effect2, 0, 40, 15, 70); + + gst_object_unref (timeline); + gst_object_unref (clip); + + ges_deinit (); +} + +GST_END_TEST; + GST_START_TEST (test_children_properties_contain) { GESTimeline *timeline; @@ -2297,7 +2603,7 @@ GST_START_TEST (test_children_properties_contain) timeline = ges_timeline_new_audio_video (); layer = ges_timeline_append_layer (timeline); clip = GES_CLIP (ges_test_clip_new ()); - ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (clip), 50); + assert_set_duration (clip, 50); fail_unless (ges_layer_add_clip (layer, clip)); @@ -2404,7 +2710,7 @@ GST_START_TEST (test_children_properties_change) timeline = ges_timeline_new_audio_video (); layer = ges_timeline_append_layer (timeline); clip = GES_TIMELINE_ELEMENT (ges_test_clip_new ()); - ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (clip), 50); + assert_set_duration (clip, 50); fail_unless (ges_layer_add_clip (layer, GES_CLIP (clip))); fail_unless (GES_CONTAINER_CHILDREN (clip)); @@ -2678,7 +2984,7 @@ GST_START_TEST (test_copy_paste_children_properties) timeline = ges_timeline_new_audio_video (); layer = ges_timeline_append_layer (timeline); clip = GES_TIMELINE_ELEMENT (ges_test_clip_new ()); - ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (clip), 50); + assert_set_duration (clip, 50); fail_unless (ges_layer_add_clip (layer, GES_CLIP (clip))); @@ -2792,6 +3098,7 @@ ges_suite (void) tcase_add_test (tc_chain, test_can_add_effect); tcase_add_test (tc_chain, test_children_inpoint); tcase_add_test (tc_chain, test_children_max_duration); + tcase_add_test (tc_chain, test_duration_limit); tcase_add_test (tc_chain, test_children_properties_contain); tcase_add_test (tc_chain, test_children_properties_change); tcase_add_test (tc_chain, test_copy_paste_children_properties); diff --git a/tests/check/ges/test-utils.h b/tests/check/ges/test-utils.h index 8208361e28..27885b69c7 100644 --- a/tests/check/ges/test-utils.h +++ b/tests/check/ges/test-utils.h @@ -106,6 +106,26 @@ G_STMT_START { \ GST_TIME_ARGS (_MAX_DURATION(obj)), GST_TIME_ARGS (max_duration)); \ } +#define assert_set_start(obj, start) \ + fail_unless (ges_timeline_element_set_start (\ + GES_TIMELINE_ELEMENT (obj), start), \ + "Could not set the start of " #obj " to " #start) + +#define assert_set_duration(obj, duration) \ + fail_unless (ges_timeline_element_set_duration (\ + GES_TIMELINE_ELEMENT (obj), duration), \ + "Could not set the duration of " #obj " to " #duration) + +#define assert_set_inpoint(obj, inpoint) \ + fail_unless (ges_timeline_element_set_inpoint (\ + GES_TIMELINE_ELEMENT (obj), inpoint), \ + "Could not set the in-point of " #obj " to " #inpoint) + +#define assert_set_max_duration(obj, max_duration) \ + fail_unless (ges_timeline_element_set_max_duration (\ + GES_TIMELINE_ELEMENT (obj), max_duration), \ + "Could not set the max-duration of " #obj " to " #max_duration) + #define assert_num_in_track(track, val) \ { \ GList *tmp = ges_track_get_elements (track); \