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: <https://gitlab.freedesktop.org/gstreamer/gst-editing-services/-/merge_requests/169>
This commit is contained in:
Henry Wilkes 2020-04-13 17:42:22 +01:00
parent 154816365f
commit 4b62749336
4 changed files with 799 additions and 161 deletions

View file

@ -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

View file

@ -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

View file

@ -22,6 +22,14 @@
#include <ges/ges.h>
#include <gst/check/gstcheck.h>
#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);

View file

@ -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); \