clip: enforce duration-limit

Prevent setting of properties or that of children, if the clip would not
be able to set the corresponding duration if the duration-limit would
drop below the currently set duration.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-editing-services/-/merge_requests/169>
This commit is contained in:
Henry Wilkes 2020-04-30 12:10:22 +01:00
parent fd353705b9
commit 0fab5f45cb
5 changed files with 1087 additions and 101 deletions

View file

@ -128,8 +128,10 @@ struct _GESClipPrivate
gboolean prevent_resort;
gboolean updating_max_duration;
gboolean prevent_max_duration_update;
gboolean setting_max_duration;
gboolean setting_inpoint;
gboolean setting_priority;
gboolean setting_active;
gboolean allow_any_track;
@ -138,6 +140,8 @@ struct _GESClipPrivate
GstClockTime duration_limit;
gboolean prevent_duration_limit_update;
gboolean allow_any_remove;
};
enum
@ -163,7 +167,10 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GESClip, ges_clip,
ges_extractable_interface_init));
/****************************************************
* *
* Listen to our children *
* and restrict them *
* *
****************************************************/
#define _IS_CORE_CHILD(child) GES_TRACK_ELEMENT_IS_CORE(child)
@ -183,6 +190,9 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GESClip, ges_clip,
(GST_CLOCK_TIME_IS_VALID (first) && (!GST_CLOCK_TIME_IS_VALID (second) \
|| first < second))
/****************************************************
* duration-limit *
****************************************************/
typedef struct _DurationLimitData
{
@ -230,6 +240,22 @@ _duration_limit_data_list (GESClip * clip)
return list;
}
/* transfer-full of data */
static GList *
_duration_limit_data_list_with_data (GESClip * clip, DurationLimitData * data)
{
GList *tmp, *list = g_list_append (NULL, data);
for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next) {
GESTrackElement *child = tmp->data;
if (data->child == child)
continue;
list = g_list_prepend (list, _duration_limit_data_new (child));
}
return list;
}
static gint
_cmp_by_track_then_priority (gconstpointer a_p, gconstpointer b_p)
{
@ -250,6 +276,7 @@ _cmp_by_track_then_priority (gconstpointer a_p, gconstpointer b_p)
((data->active && GST_CLOCK_TIME_IS_VALID (data->max_duration)) ? \
data->max_duration - data->inpoint : GST_CLOCK_TIME_NONE)
/* transfer-full of child_data */
static GstClockTime
_calculate_duration_limit (GESClip * self, GList * child_data)
{
@ -341,6 +368,30 @@ _update_duration_limit (GESClip * self)
}
}
/* transfer full of child_data */
static gboolean
_can_update_duration_limit (GESClip * self, GList * child_data)
{
GESTimeline *timeline = GES_TIMELINE_ELEMENT_TIMELINE (self);
GstClockTime duration = _calculate_duration_limit (self, child_data);
GESTimelineElement *element = GES_TIMELINE_ELEMENT (self);
if (_CLOCK_TIME_IS_LESS (duration, element->duration)) {
/* NOTE: timeline would normally not be NULL at this point */
if (timeline
&& !timeline_tree_can_move_element (timeline_get_tree (timeline),
element, ges_timeline_element_get_layer_priority (element),
element->start, duration)) {
return FALSE;
}
}
return TRUE;
}
/****************************************************
* priority *
****************************************************/
/* @min_priority: The absolute minimum priority a child of @container should have
* @max_priority: The absolute maximum priority a child of @container should have
*/
@ -367,6 +418,32 @@ _get_priority_range (GESContainer * container, guint32 * min_priority,
_PRIORITY (container));
}
gboolean
ges_clip_can_set_priority_of_child (GESClip * clip, GESTrackElement * child,
guint32 priority)
{
GList *child_data;
DurationLimitData *data;
if (clip->priv->setting_priority)
return TRUE;
data = _duration_limit_data_new (child);
data->priority = priority;
child_data = _duration_limit_data_list_with_data (clip, data);
if (!_can_update_duration_limit (clip, child_data)) {
GST_INFO_OBJECT (clip, "Cannot move the child %" GES_FORMAT " from "
"priority %" G_GUINT32_FORMAT " to %" G_GUINT32_FORMAT " because "
"the duration-limit cannot be adjusted", GES_ARGS (child),
_PRIORITY (child), priority);
return FALSE;
}
return TRUE;
}
static void
_child_priority_changed (GESContainer * container, GESTimelineElement * child)
{
@ -381,6 +458,93 @@ _child_priority_changed (GESContainer * container, GESTimelineElement * child)
}
}
/****************************************************
* in-point *
****************************************************/
static gboolean
_can_set_inpoint_of_core_children (GESClip * clip, GstClockTime inpoint)
{
GList *tmp;
GList *child_data = NULL;
if (GES_TIMELINE_ELEMENT_BEING_EDITED (clip))
return TRUE;
/* setting the in-point of a core child will shift the in-point of all
* core children with an internal source */
for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next) {
GESTimelineElement *child = tmp->data;
DurationLimitData *data =
_duration_limit_data_new (GES_TRACK_ELEMENT (child));
if (_IS_CORE_INTERNAL_SOURCE_CHILD (child)) {
if (GST_CLOCK_TIME_IS_VALID (child->maxduration)
&& child->maxduration < inpoint) {
GST_INFO_OBJECT (clip, "Cannot set the in-point from %"
G_GUINT64_FORMAT " to %" G_GUINT64_FORMAT " because it would "
"cause the in-point of its core child %" GES_FORMAT
" to exceed its max-duration", _INPOINT (clip), inpoint,
GES_ARGS (child));
_duration_limit_data_free (data);
g_list_free_full (child_data, _duration_limit_data_free);
return FALSE;
}
data->inpoint = inpoint;
}
child_data = g_list_prepend (child_data, data);
}
if (!_can_update_duration_limit (clip, child_data)) {
GST_INFO_OBJECT (clip, "Cannot set the in-point from %" G_GUINT64_FORMAT
" to %" G_GUINT64_FORMAT " because the duration-limit cannot be "
"adjusted", _INPOINT (clip), inpoint);
return FALSE;
}
return TRUE;
}
/* Whether @clip can have its in-point set to @inpoint because none of
* its children have a max-duration below it */
gboolean
ges_clip_can_set_inpoint_of_child (GESClip * clip, GESTrackElement * child,
GstClockTime inpoint)
{
/* don't bother checking if we are setting the value */
if (clip->priv->setting_inpoint)
return TRUE;
if (GES_TIMELINE_ELEMENT_BEING_EDITED (child))
return TRUE;
if (!_IS_CORE_CHILD (child)) {
/* no other sibling will move */
GList *child_data;
DurationLimitData *data = _duration_limit_data_new (child);
data->inpoint = inpoint;
child_data = _duration_limit_data_list_with_data (clip, data);
if (!_can_update_duration_limit (clip, child_data)) {
GST_INFO_OBJECT (clip, "Cannot set the in-point of non-core child %"
GES_FORMAT " from %" G_GUINT64_FORMAT " to %" G_GUINT64_FORMAT
" because the duration-limit cannot be adjusted", GES_ARGS (child),
_INPOINT (child), inpoint);
return FALSE;
}
return TRUE;
}
/* setting the in-point of a core child will shift the in-point of all
* core children with an internal source */
return _can_set_inpoint_of_core_children (clip, inpoint);
}
/* returns TRUE if duration-limit needs to be updated */
static gboolean
_child_inpoint_changed (GESClip * self, GESTimelineElement * child)
@ -406,7 +570,10 @@ _child_inpoint_changed (GESClip * self, GESTimelineElement * child)
return FALSE;
}
/* called when a child is added, removed or their max-duration changes */
/****************************************************
* max-duration *
****************************************************/
static void
_update_max_duration (GESContainer * container)
{
@ -414,7 +581,7 @@ _update_max_duration (GESContainer * container)
GstClockTime min = GST_CLOCK_TIME_NONE;
GESClipPrivate *priv = GES_CLIP (container)->priv;
if (priv->prevent_max_duration_update)
if (priv->setting_max_duration)
return;
for (tmp = container->children; tmp; tmp = tmp->next) {
@ -427,6 +594,32 @@ _update_max_duration (GESContainer * container)
priv->updating_max_duration = FALSE;
}
gboolean
ges_clip_can_set_max_duration_of_child (GESClip * clip, GESTrackElement * child,
GstClockTime max_duration)
{
GList *child_data;
DurationLimitData *data;
if (clip->priv->setting_max_duration)
return TRUE;
data = _duration_limit_data_new (child);
data->max_duration = max_duration;
child_data = _duration_limit_data_list_with_data (clip, data);
if (!_can_update_duration_limit (clip, child_data)) {
GST_INFO_OBJECT (clip, "Cannot set the max-duration of child %"
GES_FORMAT " from %" G_GUINT64_FORMAT " to %" G_GUINT64_FORMAT
" because the duration-limit cannot be adjusted", GES_ARGS (child),
_MAXDURATION (child), max_duration);
return FALSE;
}
return TRUE;
}
static void
_child_max_duration_changed (GESContainer * container,
GESTimelineElement * child)
@ -438,20 +631,80 @@ _child_max_duration_changed (GESContainer * container,
_update_max_duration (container);
}
/****************************************************
* has-internal-source *
****************************************************/
static void
_child_has_internal_source_changed (GESClip * self, GESTimelineElement * child)
{
/* ignore non-core */
/* if the track element is now registered to have no internal content,
* we don't have to do anything */
* we don't have to do anything
* Note that the change in max-duration and in-point will already trigger
* a change in the duration-limit, which can only increase since the
* max-duration is now GST_CLOCK_TIME_NONE */
if (!_IS_CORE_INTERNAL_SOURCE_CHILD (child))
return;
/* otherwise, we need to make its in-point match ours */
/* otherwise, we need to make its in-point match ours
* Note that the duration-limit will be GST_CLOCK_TIME_NONE, so this
* should not change the duration-limit */
_set_inpoint0 (child, _INPOINT (self));
}
#define _IS_PROP(prop) (g_strcmp0 (name, prop) == 0)
/****************************************************
* active *
****************************************************/
gboolean
ges_clip_can_set_active_of_child (GESClip * clip, GESTrackElement * child,
gboolean active)
{
GESTrack *track = ges_track_element_get_track (child);
gboolean is_core = _IS_CORE_CHILD (child);
GList *child_data = NULL;
DurationLimitData *data;
if (clip->priv->setting_active)
return TRUE;
/* We want to ensure that each active non-core element has a
* corresponding active core element in the same track */
if (!track || is_core == active) {
/* only the one child will change */
data = _duration_limit_data_new (child);
data->active = active;
child_data = _duration_limit_data_list_with_data (clip, data);
} else {
GList *tmp;
/* If we are core, make all the non-core elements in-active
* If we are non-core, make the core element active */
for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next) {
GESTrackElement *sibling = tmp->data;
data = _duration_limit_data_new (sibling);
if (sibling == child)
data->active = active;
if (ges_track_element_get_track (sibling) == track
&& _IS_CORE_CHILD (sibling) != is_core
&& ges_track_element_is_active (sibling) != active)
data->active = active;
child_data = g_list_prepend (child_data, data);
}
}
if (!_can_update_duration_limit (clip, child_data)) {
GST_INFO_OBJECT (clip, "Cannot set the active of child %" GES_FORMAT
" from %i to %i because the duration-limit cannot be adjusted",
GES_ARGS (child), ges_track_element_is_active (child), active);
return FALSE;
}
return TRUE;
}
static void
_child_active_changed (GESClip * self, GESTrackElement * child)
@ -467,6 +720,7 @@ _child_active_changed (GESClip * self, GESTrackElement * child)
if (self->priv->setting_active || !track || is_core == active)
return;
self->priv->setting_active = TRUE;
self->priv->prevent_duration_limit_update = TRUE;
/* If we are core, make all the non-core elements in-active
@ -489,11 +743,12 @@ _child_active_changed (GESClip * self, GESTrackElement * child)
}
}
self->priv->setting_active = FALSE;
self->priv->prevent_duration_limit_update = prev_prevent;
}
/****************************************************
* Restrict our children *
* track *
****************************************************/
static GESTrackElement *
@ -524,6 +779,8 @@ gboolean
ges_clip_can_set_track_of_child (GESClip * clip, GESTrackElement * child,
GESTrack * track)
{
GList *child_data;
DurationLimitData *data;
GESTrack *current_track = ges_track_element_get_track (child);
GESTrackElement *core = NULL;
@ -584,6 +841,28 @@ ges_clip_can_set_track_of_child (GESClip * clip, GESTrackElement * child,
}
}
}
data = _duration_limit_data_new (child);
gst_clear_object (&data->track);
data->track = track ? gst_object_ref (track) : NULL;
if (core && !ges_track_element_is_active (core)) {
/* if core is set, then we are adding a non-core to a track containing
* a core track element. If this happens, but the core is in-active
* then we will make the non-core element also inactive upon setting
* its track */
data->active = FALSE;
}
child_data = _duration_limit_data_list_with_data (clip, data);
if (!_can_update_duration_limit (clip, child_data)) {
GST_INFO_OBJECT (clip, "Cannot move the child %" GES_FORMAT " from "
"track %" GST_PTR_FORMAT " to track %" GST_PTR_FORMAT " because "
"the duration-limit cannot be adjusted", GES_ARGS (child),
current_track, track);
return FALSE;
}
return TRUE;
}
@ -617,12 +896,14 @@ _update_active_for_track (GESClip * self, GESTrackElement * child)
" since the core child in the same track %" GST_PTR_FORMAT " is "
"not active", GES_ARGS (child), track);
self->priv->setting_active = TRUE;
self->priv->prevent_duration_limit_update = TRUE;
if (!ges_track_element_set_active (child, FALSE))
GST_ERROR_OBJECT (self, "Failed to de-activate child %" GES_FORMAT,
GES_ARGS (child));
self->priv->setting_active = FALSE;
self->priv->prevent_duration_limit_update = prev_prevent;
}
}
@ -688,33 +969,6 @@ _set_start (GESTimelineElement * element, GstClockTime start)
return TRUE;
}
/* Whether @clip can have its in-point set to @inpoint because none of
* its children have a max-duration below it */
gboolean
ges_clip_can_set_inpoint_of_child (GESClip * clip, GESTimelineElement * child,
GstClockTime inpoint)
{
GList *tmp;
/* don't bother checking if we are setting the value */
if (clip->priv->setting_inpoint)
return TRUE;
/* non-core children do not effect our in-point */
if (!_IS_CORE_CHILD (child))
return TRUE;
for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next) {
GESTimelineElement *child = tmp->data;
if (_IS_CORE_INTERNAL_SOURCE_CHILD (child)
&& GST_CLOCK_TIME_IS_VALID (child->maxduration)
&& child->maxduration < inpoint)
return FALSE;
}
return TRUE;
}
/* returns TRUE if we did not break early */
static gboolean
_set_childrens_inpoint (GESTimelineElement * element, GstClockTime inpoint,
@ -754,6 +1008,12 @@ _set_childrens_inpoint (GESTimelineElement * element, GstClockTime inpoint,
static gboolean
_set_inpoint (GESTimelineElement * element, GstClockTime inpoint)
{
if (!_can_set_inpoint_of_core_children (GES_CLIP (element), inpoint)) {
GST_WARNING_OBJECT (element, "Cannot set the in-point to %"
GST_TIME_FORMAT, GST_TIME_ARGS (inpoint));
return FALSE;
}
if (!_set_childrens_inpoint (element, inpoint, TRUE)) {
_set_childrens_inpoint (element, element->inpoint, FALSE);
return FALSE;
@ -786,6 +1046,7 @@ static gboolean
_set_max_duration (GESTimelineElement * element, GstClockTime maxduration)
{
GList *tmp;
GList *child_data = NULL;
GESClip *self = GES_CLIP (element);
GESClipPrivate *priv = self->priv;
GstClockTime new_min = GST_CLOCK_TIME_NONE;
@ -799,8 +1060,26 @@ _set_max_duration (GESTimelineElement * element, GstClockTime maxduration)
/* else, we set every core child to have the same max duration */
/* check that the duration-limit can be changed */
for (tmp = GES_CONTAINER_CHILDREN (self); tmp; tmp = tmp->next) {
GESTrackElement *child = tmp->data;
DurationLimitData *data = _duration_limit_data_new (child);
if (_IS_CORE_INTERNAL_SOURCE_CHILD (child))
data->max_duration = maxduration;
child_data = g_list_prepend (child_data, data);
}
if (!_can_update_duration_limit (self, child_data)) {
GST_WARNING_OBJECT (self, "Cannot set the max-duration from %"
G_GUINT64_FORMAT " to %" G_GUINT64_FORMAT " because the "
"duration-limit cannot be adjusted", element->maxduration, maxduration);
return FALSE;
}
priv->prevent_duration_limit_update = TRUE;
priv->prevent_max_duration_update = TRUE;
priv->setting_max_duration = TRUE;
for (tmp = GES_CONTAINER_CHILDREN (element); tmp; tmp = tmp->next) {
GESTimelineElement *child = tmp->data;
@ -815,7 +1094,7 @@ _set_max_duration (GESTimelineElement * element, GstClockTime maxduration)
}
}
}
priv->prevent_max_duration_update = FALSE;
priv->setting_max_duration = FALSE;
priv->prevent_duration_limit_update = prev_prevent;
if (!has_core) {
@ -874,6 +1153,7 @@ _set_priority (GESTimelineElement * element, guint32 priority)
/* offsets will remain constant for the children */
priv->prevent_resort = TRUE;
priv->prevent_duration_limit_update = TRUE;
priv->setting_priority = TRUE;
for (tmp = container->children; tmp; tmp = g_list_next (tmp)) {
GESTimelineElement *child = tmp->data;
guint32 track_element_prio = min_prio + (child->priority - min_child_prio);
@ -891,6 +1171,7 @@ _set_priority (GESTimelineElement * element, guint32 priority)
* offsets. As such, the height and duration-limit remains the same as
* well. */
priv->prevent_resort = FALSE;
priv->setting_priority = FALSE;
priv->prevent_duration_limit_update = prev_prevent;
return TRUE;
@ -1010,33 +1291,19 @@ _add_child (GESContainer * container, GESTimelineElement * element)
/* NOTE: Core track elements that are base effects are added like any
* other core elements. In particular, they are *not* added to the
* list of added effects, so we do not increase nb_effects. */
GESTrackElement *core = (track && !priv->allow_any_track) ?
_find_core_in_track (self, track) : NULL;
if (core) {
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_FORMAT, GES_ARGS (element), track,
GES_ARGS (core));
return FALSE;
}
/* Set the core element to have the same in-point, which we don't
* apply to effects */
if (ges_track_element_has_internal_source (track_el)) {
/* 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 (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 (self)));
return FALSE;
}
}
GstClockTime new_inpoint;
if (ges_track_element_has_internal_source (track_el))
new_inpoint = _INPOINT (self);
else
new_inpoint = 0;
/* new priority is that of the lowest priority core child. Usually
* each core child has the same priority.
* Also must be lower than all effects */
new_prio = min_prio;
for (tmp = container->children; tmp; tmp = tmp->next) {
if (_IS_CORE_CHILD (tmp->data))
@ -1044,20 +1311,50 @@ _add_child (GESContainer * container, GESTimelineElement * element)
else if (_IS_TOP_EFFECT (tmp->data))
new_prio = MAX (new_prio, _PRIORITY (tmp->data) + 1);
}
if (track && !priv->allow_any_track) {
GList *child_data;
DurationLimitData *data;
GESTrackElement *core = _find_core_in_track (self, track);
if (core) {
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_FORMAT, GES_ARGS (element), track,
GES_ARGS (core));
return FALSE;
}
data = _duration_limit_data_new (track_el);
data->inpoint = new_inpoint;
data->priority = new_prio;
child_data = _duration_limit_data_list_with_data (self, data);
if (!_can_update_duration_limit (self, child_data)) {
GST_WARNING_OBJECT (self, "Cannot add core %" GES_FORMAT " as a "
"child because the duration-limit cannot be adjusted",
GES_ARGS (element));
return FALSE;
}
}
/* adding can fail if the max-duration of the element is smaller
* than the current in-point of the clip */
if (!_set_inpoint0 (element, new_inpoint)) {
GST_WARNING_OBJECT (self, "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 (new_inpoint));
return FALSE;
}
_set_priority0 (element, new_prio);
} else if (GES_CLIP_CLASS_CAN_ADD_EFFECTS (klass) && _IS_TOP_EFFECT (element)) {
/* Add the effect at the lowest priority among effects (just after
* the core elements). Need to shift the core elements up by 1
* to make room. */
if (track && !priv->allow_any_track && !_find_core_in_track (self, track)) {
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;
}
_update_active_for_track (self, track_el);
/* new priority is the lowest priority effect */
new_prio = min_prio;
for (tmp = container->children; tmp; tmp = tmp->next) {
@ -1070,12 +1367,48 @@ _add_child (GESContainer * container, GESTimelineElement * element)
new_prio = MIN (new_prio, _PRIORITY (tmp->data));
}
if (track && !priv->allow_any_track) {
GList *child_data, *tmp;
DurationLimitData *data;
GESTrackElement *core = _find_core_in_track (self, track);
if (!core) {
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;
}
data = _duration_limit_data_new (track_el);
data->priority = new_prio;
if (!ges_track_element_is_active (core))
data->active = FALSE;
child_data = _duration_limit_data_list_with_data (self, data);
for (tmp = child_data; tmp; tmp = tmp->next) {
data = tmp->data;
if (data->priority >= new_prio)
data->priority++;
}
if (!_can_update_duration_limit (self, child_data)) {
GST_WARNING_OBJECT (self, "Cannot add effect %" GES_FORMAT " as "
"a child because the duration-limit cannot be adjusted",
GES_ARGS (element));
return FALSE;
}
}
_update_active_for_track (self, track_el);
priv->nb_effects++;
GST_DEBUG_OBJECT (self, "Adding %ith effect: %" GES_FORMAT
" Priority %i", priv->nb_effects, GES_ARGS (element), new_prio);
/* changing priorities, and updating their offset */
priv->prevent_resort = TRUE;
priv->setting_priority = TRUE;
priv->prevent_duration_limit_update = TRUE;
/* increase the priority of anything with a lower priority */
@ -1087,6 +1420,7 @@ _add_child (GESContainer * container, GESTimelineElement * element)
_set_priority0 (element, new_prio);
priv->prevent_resort = FALSE;
priv->setting_priority = 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
@ -1120,7 +1454,34 @@ _add_child (GESContainer * container, GESTimelineElement * element)
static gboolean
_remove_child (GESContainer * container, GESTimelineElement * element)
{
GESClipPrivate *priv = GES_CLIP (container)->priv;
GESTrackElement *el = GES_TRACK_ELEMENT (element);
GESClip *self = GES_CLIP (container);
GESClipPrivate *priv = self->priv;
/* check that the duration-limit can be changed */
/* If we are removing a core child, then all other children in the
* same track will be removed from the track, which will make the
* duration-limit increase, which is safe
* Similarly, if it has no track, the duration-limit will not change */
if (!priv->allow_any_remove && !_IS_CORE_CHILD (element) &&
ges_track_element_get_track (el)) {
GList *child_data = NULL;
GList *tmp;
for (tmp = container->children; tmp; tmp = tmp->next) {
GESTrackElement *child = tmp->data;
if (child == el)
continue;
child_data =
g_list_prepend (child_data, _duration_limit_data_new (child));
}
if (!_can_update_duration_limit (self, child_data)) {
GST_WARNING_OBJECT (self, "Cannot remove the child %" GES_FORMAT
" because the duration-limit cannot be adjusted", GES_ARGS (el));
return FALSE;
}
}
/* NOTE: notifies are currently frozen by ges_container_add */
if (_IS_TOP_EFFECT (element)) {
@ -1130,6 +1491,7 @@ _remove_child (GESContainer * container, GESTimelineElement * element)
/* changing priorities, so preventing a re-sort */
priv->prevent_resort = TRUE;
priv->setting_priority = TRUE;
priv->prevent_duration_limit_update = TRUE;
for (tmp = container->children; tmp; tmp = tmp->next) {
guint32 sibling_prio = GES_TIMELINE_ELEMENT_PRIORITY (tmp->data);
@ -1139,6 +1501,7 @@ _remove_child (GESContainer * container, GESTimelineElement * element)
}
priv->nb_effects--;
priv->prevent_resort = FALSE;
priv->setting_priority = FALSE;
priv->prevent_duration_limit_update = prev_prevent;
/* no need to re-sort the children since the rest keep the same
* relative priorities */
@ -1212,8 +1575,10 @@ _transfer_child (GESClip * from_clip, GESClip * to_clip,
from_clip->priv->prevent_duration_limit_update = TRUE;
to_clip->priv->prevent_duration_limit_update = TRUE;
from_clip->priv->allow_any_remove = TRUE;
ges_container_remove (GES_CONTAINER (from_clip),
GES_TIMELINE_ELEMENT (child));
from_clip->priv->allow_any_remove = FALSE;
to_clip->priv->allow_any_track = TRUE;
if (!ges_container_add (GES_CONTAINER (to_clip),
@ -1669,6 +2034,8 @@ ges_clip_dispose (GObject * object)
{
GESClip *self = GES_CLIP (object);
self->priv->allow_any_remove = TRUE;
g_list_free_full (self->priv->copied_track_elements, gst_object_unref);
self->priv->copied_track_elements = NULL;
g_clear_object (&self->priv->copied_layer);
@ -2261,11 +2628,35 @@ ges_clip_set_top_effect_index (GESClip * clip, GESBaseEffect * effect,
else
inc = +1;
/* check that the duration-limit can be changed */
for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next) {
GESTimelineElement *child = tmp->data;
guint32 priority = child->priority;
DurationLimitData *data =
_duration_limit_data_new (GES_TRACK_ELEMENT (child));
if (child == element)
data->priority = new_prio;
else if ((inc == +1 && priority >= new_prio && priority < current_prio)
|| (inc == -1 && priority <= new_prio && priority > current_prio))
data->priority = child->priority + inc;
child_data = g_list_prepend (child_data, data);
}
if (!_can_update_duration_limit (clip, child_data)) {
GST_WARNING_OBJECT (clip, "Cannot move top effect %" GES_FORMAT
" to index %i because the duration-limit cannot adjust",
GES_ARGS (effect), newindex);
return FALSE;
}
GST_DEBUG_OBJECT (clip, "Setting top effect %" GST_PTR_FORMAT "priority: %i",
effect, new_prio);
/* prevent a re-sort of the list whilst we are traversing it! */
clip->priv->prevent_resort = TRUE;
clip->priv->setting_priority = TRUE;
for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next) {
GESTimelineElement *child = tmp->data;
guint32 priority = child->priority;
@ -2282,6 +2673,7 @@ ges_clip_set_top_effect_index (GESClip * clip, GESBaseEffect * effect,
_set_priority0 (element, new_prio);
clip->priv->prevent_resort = FALSE;
clip->priv->setting_priority = FALSE;
_ges_container_sort_children (GES_CONTAINER (clip));
/* height should have stayed the same */

View file

@ -404,7 +404,10 @@ G_GNUC_INTERNAL gboolean ges_clip_is_moving_from_layer (GESClip *clip
G_GNUC_INTERNAL void ges_clip_set_moving_from_layer (GESClip *clip, gboolean is_moving);
G_GNUC_INTERNAL GESTrackElement* ges_clip_create_track_element (GESClip *clip, GESTrackType type);
G_GNUC_INTERNAL GList* ges_clip_create_track_elements (GESClip *clip, GESTrackType type);
G_GNUC_INTERNAL gboolean ges_clip_can_set_inpoint_of_child (GESClip * clip, GESTimelineElement * child, GstClockTime inpoint);
G_GNUC_INTERNAL gboolean ges_clip_can_set_inpoint_of_child (GESClip * clip, GESTrackElement * child, GstClockTime inpoint);
G_GNUC_INTERNAL gboolean ges_clip_can_set_max_duration_of_child (GESClip * clip, GESTrackElement * child, GstClockTime max_duration);
G_GNUC_INTERNAL gboolean ges_clip_can_set_active_of_child (GESClip * clip, GESTrackElement * child, gboolean active);
G_GNUC_INTERNAL gboolean ges_clip_can_set_priority_of_child (GESClip * clip, GESTrackElement * child, guint32 priority);
G_GNUC_INTERNAL gboolean ges_clip_can_set_track_of_child (GESClip * clip, GESTrackElement * child, GESTrack * tack);
G_GNUC_INTERNAL void ges_clip_empty_from_track (GESClip * clip, GESTrack * track);

View file

@ -629,21 +629,21 @@ static gboolean
_set_inpoint (GESTimelineElement * element, GstClockTime inpoint)
{
GESTrackElement *object = GES_TRACK_ELEMENT (element);
GESTimelineElement *parent = element->parent;
g_return_val_if_fail (object->priv->nleobject, FALSE);
if (inpoint && !object->priv->has_internal_source) {
GST_WARNING_OBJECT (element, "Can not set an in-point for a track "
GST_WARNING_OBJECT (element, "Cannot set an in-point for a track "
"element that is not registered with internal content");
return FALSE;
}
if (GES_IS_CLIP (element->parent)
&& !ges_clip_can_set_inpoint_of_child (GES_CLIP (element->parent),
element, inpoint)) {
GST_WARNING_OBJECT (element, "Can not set an in-point of %"
if (GES_IS_CLIP (parent)
&& !ges_clip_can_set_inpoint_of_child (GES_CLIP (parent), object,
inpoint)) {
GST_WARNING_OBJECT (element, "Cannot set an in-point of %"
GST_TIME_FORMAT " because the parent clip %" GES_FORMAT
" would not be able to follow",
GST_TIME_ARGS (inpoint), GES_ARGS (element->parent));
" would not allow it", GST_TIME_ARGS (inpoint), GES_ARGS (parent));
return FALSE;
}
@ -677,14 +677,24 @@ static gboolean
_set_max_duration (GESTimelineElement * element, GstClockTime max_duration)
{
GESTrackElement *object = GES_TRACK_ELEMENT (element);
GESTimelineElement *parent = element->parent;
if (GST_CLOCK_TIME_IS_VALID (max_duration)
&& !object->priv->has_internal_source) {
GST_WARNING_OBJECT (element, "Can not set a max-duration for a track "
GST_WARNING_OBJECT (element, "Cannot set a max-duration for a track "
"element that is not registered with internal content");
return FALSE;
}
if (GES_IS_CLIP (parent)
&& !ges_clip_can_set_max_duration_of_child (GES_CLIP (parent), object,
max_duration)) {
GST_WARNING_OBJECT (element, "Cannot set a max-duration of %"
GST_TIME_FORMAT " because the parent clip %" GES_FORMAT
" would not allow it", GST_TIME_ARGS (max_duration), GES_ARGS (parent));
return FALSE;
}
return TRUE;
}
@ -693,6 +703,7 @@ static gboolean
_set_priority (GESTimelineElement * element, guint32 priority)
{
GESTrackElement *object = GES_TRACK_ELEMENT (element);
GESTimelineElement *parent = element->parent;
g_return_val_if_fail (object->priv->nleobject, FALSE);
@ -707,6 +718,15 @@ _set_priority (GESTimelineElement * element, guint32 priority)
if (G_UNLIKELY (priority == _PRIORITY (object)))
return FALSE;
if (GES_IS_CLIP (parent)
&& !ges_clip_can_set_priority_of_child (GES_CLIP (parent), object,
priority)) {
GST_WARNING_OBJECT (element, "Cannot set a priority of %"
G_GUINT32_FORMAT " because the parent clip %" GES_FORMAT
" would not allow it", priority, GES_ARGS (parent));
return FALSE;
}
g_object_set (object->priv->nleobject, "priority", priority, NULL);
return TRUE;
@ -730,6 +750,7 @@ _get_track_types (GESTimelineElement * object)
gboolean
ges_track_element_set_active (GESTrackElement * object, gboolean active)
{
GESTimelineElement *parent;
g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), FALSE);
g_return_val_if_fail (object->priv->nleobject, FALSE);
@ -738,6 +759,14 @@ ges_track_element_set_active (GESTrackElement * object, gboolean active)
if (G_UNLIKELY (active == object->active))
return FALSE;
parent = GES_TIMELINE_ELEMENT_PARENT (object);
if (GES_IS_CLIP (parent)
&& !ges_clip_can_set_active_of_child (GES_CLIP (parent), object, active)) {
GST_WARNING_OBJECT (object, "Cannot set active to %i because the parent "
"clip %" GES_FORMAT " would not allow it", active, GES_ARGS (parent));
return FALSE;
}
g_object_set (object->priv->nleobject, "active",
active & object->priv->layer_active, NULL);

View file

@ -2316,7 +2316,7 @@ GST_START_TEST (test_children_inpoint)
CHECK_OBJECT_PROPS (effect, 5, 67, 20);
/* should not be able to set the in-point to non-zero */
fail_if (ges_timeline_element_set_inpoint (child0, 40));
assert_fail_set_inpoint (child0, 40);
CHECK_OBJECT_PROPS (clip, 5, 30, 20);
CHECK_OBJECT_PROPS (child0, 5, 0, 20);
@ -2506,21 +2506,21 @@ GST_START_TEST (test_children_max_duration)
/* can not set in-point above max_duration, nor max_duration below
* in-point */
fail_if (ges_timeline_element_set_max_duration (child0, 29));
assert_fail_set_max_duration (child0, 29);
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_if (ges_timeline_element_set_max_duration (child1, 29));
assert_fail_set_max_duration (child1, 29);
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_if (ges_timeline_element_set_max_duration (clip, 29));
assert_fail_set_max_duration (clip, 29);
CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, new_max - 2);
CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, new_max + 1);
@ -2530,21 +2530,21 @@ GST_START_TEST (test_children_max_duration)
/* can't set the inpoint to (new_max), even though it is lower than
* our own max-duration (new_max + 1) because it is too high for our
* sibling child1 */
fail_if (ges_timeline_element_set_inpoint (child0, new_max));
assert_fail_set_inpoint (child0, new_max);
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_if (ges_timeline_element_set_inpoint (child1, new_max));
assert_fail_set_inpoint (child1, new_max);
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_if (ges_timeline_element_set_inpoint (clip, new_max));
assert_fail_set_inpoint (clip, new_max);
CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, new_max - 2);
CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, new_max + 1);
@ -2591,14 +2591,14 @@ GST_START_TEST (test_children_max_duration)
CHECK_OBJECT_PROPS_MAX (effect, 5, new_max + 2, 20, new_max + 500);
/* but not higher than our own */
fail_if (ges_timeline_element_set_inpoint (effect, new_max + 501));
assert_fail_set_inpoint (effect, new_max + 501);
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, new_max + 2, 20, new_max + 500);
fail_if (ges_timeline_element_set_max_duration (effect, new_max + 1));
assert_fail_set_max_duration (effect, 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);
@ -2640,7 +2640,7 @@ GST_START_TEST (test_children_max_duration)
CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 400);
/* should not be able to set the max-duration to a valid time */
fail_if (ges_timeline_element_set_max_duration (child0, 40));
assert_fail_set_max_duration (child0, 40);
CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, 180);
CHECK_OBJECT_PROPS_MAX (child0, 5, 0, 20, GST_CLOCK_TIME_NONE);
@ -2659,7 +2659,7 @@ GST_START_TEST (test_children_max_duration)
/* should not be able to set the max of the clip to anything else
* when it has no core children with an internal source */
fail_if (ges_timeline_element_set_max_duration (clip, 150));
assert_fail_set_max_duration (clip, 150);
CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (child0, 5, 0, 20, GST_CLOCK_TIME_NONE);
@ -3060,6 +3060,549 @@ GST_START_TEST (test_duration_limit)
GST_END_TEST;
GST_START_TEST (test_can_set_duration_limit)
{
GESTimeline *timeline;
GESLayer *layer;
GESClip *clip;
GESTrackElement *source0, *source1;
GESTrackElement *effect0, *effect1, *effect2;
GESTrack *track0, *track1;
gint limit_notify_count = 0;
ges_init ();
timeline = ges_timeline_new ();
track0 = GES_TRACK (ges_video_track_new ());
track1 = GES_TRACK (ges_audio_track_new ());
fail_unless (ges_timeline_add_track (timeline, track0));
fail_unless (ges_timeline_add_track (timeline, track1));
/* add track3 later */
layer = ges_timeline_append_layer (timeline);
/* place a dummy clip at the start of the layer */
clip = GES_CLIP (ges_test_clip_new ());
assert_set_start (clip, 0);
assert_set_duration (clip, 20);
fail_unless (ges_layer_add_clip (layer, clip));
/* the clip we will be editing overlaps the first clip at its start */
clip = GES_CLIP (ges_test_clip_new ());
g_signal_connect (clip, "notify::duration-limit", G_CALLBACK (_count_cb),
&limit_notify_count);
assert_set_start (clip, 10);
assert_set_duration (clip, 20);
fail_unless (ges_layer_add_clip (layer, clip));
source0 =
ges_clip_find_track_element (clip, track0, GES_TYPE_VIDEO_TEST_SOURCE);
source1 =
ges_clip_find_track_element (clip, track1, GES_TYPE_AUDIO_TEST_SOURCE);
fail_unless (source0);
fail_unless (source1);
gst_object_unref (source0);
gst_object_unref (source1);
assert_equals_int (limit_notify_count, 0);
_assert_duration_limit (clip, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (clip, 10, 0, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (source0, 10, 0, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (source1, 10, 0, 20, GST_CLOCK_TIME_NONE);
assert_set_inpoint (clip, 16);
assert_equals_int (limit_notify_count, 0);
_assert_duration_limit (clip, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (clip, 10, 16, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (source0, 10, 16, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (source1, 10, 16, 20, GST_CLOCK_TIME_NONE);
assert_set_max_duration (clip, 36);
assert_equals_int (limit_notify_count, 1);
_assert_duration_limit (clip, 20);
CHECK_OBJECT_PROPS_MAX (clip, 10, 16, 20, 36);
CHECK_OBJECT_PROPS_MAX (source0, 10, 16, 20, 36);
CHECK_OBJECT_PROPS_MAX (source1, 10, 16, 20, 36);
/* add effects */
effect0 = GES_TRACK_ELEMENT (ges_effect_new ("agingtv"));
effect1 = GES_TRACK_ELEMENT (ges_effect_new ("vertigotv"));
effect2 = GES_TRACK_ELEMENT (ges_effect_new ("alpha"));
ges_track_element_set_has_internal_source (effect0, TRUE);
fail_unless (ges_track_element_has_internal_source (effect1) == FALSE);
ges_track_element_set_has_internal_source (effect2, TRUE);
assert_set_max_duration (effect0, 10);
/* already set the track */
fail_unless (ges_track_add_element (track0, effect0));
/* cannot add because it would cause the duration-limit to go to 10,
* causing a full overlap with the clip at the beginning of the layer */
gst_object_ref (effect0);
fail_if (ges_container_add (GES_CONTAINER (clip),
GES_TIMELINE_ELEMENT (effect0)));
assert_equals_int (limit_notify_count, 1);
_assert_duration_limit (clip, 20);
CHECK_OBJECT_PROPS_MAX (clip, 10, 16, 20, 36);
CHECK_OBJECT_PROPS_MAX (source0, 10, 16, 20, 36);
CHECK_OBJECT_PROPS_MAX (source1, 10, 16, 20, 36);
/* removing from the track and adding will work, but track selection
* will fail */
fail_unless (ges_track_remove_element (track0, effect0));
_assert_add (clip, effect0);
fail_if (ges_track_element_get_track (effect0));
gst_object_unref (effect0);
assert_equals_int (limit_notify_count, 1);
_assert_duration_limit (clip, 20);
CHECK_OBJECT_PROPS_MAX (clip, 10, 16, 20, 36);
CHECK_OBJECT_PROPS_MAX (source0, 10, 16, 20, 36);
CHECK_OBJECT_PROPS_MAX (source1, 10, 16, 20, 36);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 10);
fail_if (ges_clip_add_child_to_track (clip, effect0, track0, NULL));
/* set max-duration to 11 and we are fine to select a track */
assert_set_max_duration (effect0, 11);
assert_equals_int (limit_notify_count, 1);
_assert_duration_limit (clip, 20);
fail_unless (ges_clip_add_child_to_track (clip, effect0, track0,
NULL) == effect0);
assert_equals_int (limit_notify_count, 2);
_assert_duration_limit (clip, 11);
CHECK_OBJECT_PROPS_MAX (clip, 10, 16, 11, 36);
CHECK_OBJECT_PROPS_MAX (source0, 10, 16, 11, 36);
CHECK_OBJECT_PROPS_MAX (source1, 10, 16, 11, 36);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 11, 11);
/* cannot set duration above the limit */
assert_fail_set_duration (clip, 12);
assert_fail_set_duration (source0, 12);
assert_fail_set_duration (effect0, 12);
/* allow the max_duration to increase again */
assert_set_max_duration (effect0, 25);
assert_equals_int (limit_notify_count, 3);
_assert_duration_limit (clip, 20);
CHECK_OBJECT_PROPS_MAX (clip, 10, 16, 11, 36);
CHECK_OBJECT_PROPS_MAX (source0, 10, 16, 11, 36);
CHECK_OBJECT_PROPS_MAX (source1, 10, 16, 11, 36);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 11, 25);
assert_set_duration (clip, 20);
assert_equals_int (limit_notify_count, 3);
_assert_duration_limit (clip, 20);
CHECK_OBJECT_PROPS_MAX (clip, 10, 16, 20, 36);
CHECK_OBJECT_PROPS_MAX (source0, 10, 16, 20, 36);
CHECK_OBJECT_PROPS_MAX (source1, 10, 16, 20, 36);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
/* add another effect */
_assert_add (clip, effect1);
fail_unless (ges_track_element_get_track (effect1) == track0);
assert_equals_int (limit_notify_count, 3);
_assert_duration_limit (clip, 20);
CHECK_OBJECT_PROPS_MAX (clip, 10, 16, 20, 36);
CHECK_OBJECT_PROPS_MAX (source0, 10, 16, 20, 36);
CHECK_OBJECT_PROPS_MAX (source1, 10, 16, 20, 36);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
/* make source0 inactive and reduce its max-duration
* note that this causes effect0 and effect1 to also become in-active */
_assert_set_active (source0, FALSE);
_assert_active (source0, FALSE);
_assert_active (effect0, FALSE);
_assert_active (effect1, FALSE);
assert_equals_int (limit_notify_count, 3);
_assert_duration_limit (clip, 20);
CHECK_OBJECT_PROPS_MAX (clip, 10, 16, 20, 36);
CHECK_OBJECT_PROPS_MAX (source0, 10, 16, 20, 36);
CHECK_OBJECT_PROPS_MAX (source1, 10, 16, 20, 36);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
/* can set a low max duration whilst the source is inactive */
assert_set_max_duration (source0, 26);
assert_equals_int (limit_notify_count, 3);
_assert_duration_limit (clip, 20);
CHECK_OBJECT_PROPS_MAX (clip, 10, 16, 20, 26);
CHECK_OBJECT_PROPS_MAX (source0, 10, 16, 20, 26);
CHECK_OBJECT_PROPS_MAX (source1, 10, 16, 20, 36);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
/* add the last effect */
assert_set_inpoint (effect2, 7);
assert_set_max_duration (effect2, 17);
_assert_active (effect2, TRUE);
/* safe to add because the source is inactive */
assert_equals_int (limit_notify_count, 3);
_assert_add (clip, effect2);
assert_equals_int (limit_notify_count, 3);
_assert_active (source0, FALSE);
_assert_active (effect0, FALSE);
_assert_active (effect1, FALSE);
_assert_active (effect2, FALSE);
fail_unless (ges_track_element_get_track (effect2) == track0);
assert_equals_int (limit_notify_count, 3);
_assert_duration_limit (clip, 20);
CHECK_OBJECT_PROPS_MAX (clip, 10, 16, 20, 26);
CHECK_OBJECT_PROPS_MAX (source0, 10, 16, 20, 26);
CHECK_OBJECT_PROPS_MAX (source1, 10, 16, 20, 36);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 20, 17);
/* want to make the source and its effects active again */
assert_set_inpoint (source0, 6);
assert_set_max_duration (effect2, 33);
assert_equals_int (limit_notify_count, 4);
_assert_duration_limit (clip, 30);
CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 36);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 20, 33);
_assert_set_active (source0, TRUE);
_assert_set_active (effect0, TRUE);
_assert_set_active (effect1, TRUE);
_assert_set_active (effect2, TRUE);
assert_equals_int (limit_notify_count, 5);
_assert_duration_limit (clip, 20);
CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 36);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 20, 33);
/* cannot set in-point of clip to 16, nor of either source */
assert_fail_set_inpoint (clip, 16);
assert_fail_set_inpoint (source0, 16);
assert_fail_set_inpoint (source1, 16);
assert_equals_int (limit_notify_count, 5);
_assert_duration_limit (clip, 20);
CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 36);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 20, 33);
/* can set just below */
assert_set_inpoint (source1, 15);
assert_equals_int (limit_notify_count, 6);
_assert_duration_limit (clip, 11);
CHECK_OBJECT_PROPS_MAX (clip, 10, 15, 11, 26);
CHECK_OBJECT_PROPS_MAX (source0, 10, 15, 11, 26);
CHECK_OBJECT_PROPS_MAX (source1, 10, 15, 11, 36);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 11, 25);
CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 11, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 11, 33);
assert_set_inpoint (clip, 6);
assert_set_duration (clip, 20);
assert_equals_int (limit_notify_count, 7);
_assert_duration_limit (clip, 20);
CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 36);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 20, 33);
/* cannot set in-point of non-core in a way that would cause limit to
* drop */
assert_fail_set_inpoint (effect2, 23);
assert_fail_set_inpoint (effect0, 15);
assert_equals_int (limit_notify_count, 7);
_assert_duration_limit (clip, 20);
CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 36);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 20, 33);
/* can set just below */
assert_set_inpoint (effect2, 22);
assert_equals_int (limit_notify_count, 8);
_assert_duration_limit (clip, 11);
CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 11, 26);
CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 11, 26);
CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 11, 36);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 11, 25);
CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 11, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (effect2, 10, 22, 11, 33);
assert_set_inpoint (effect2, 7);
assert_set_duration (clip, 20);
assert_equals_int (limit_notify_count, 9);
_assert_duration_limit (clip, 20);
CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 36);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 20, 33);
/* same but with max-duration */
/* core */
assert_fail_set_max_duration (clip, 16);
assert_fail_set_max_duration (source0, 16);
assert_fail_set_max_duration (source1, 16);
assert_equals_int (limit_notify_count, 9);
_assert_duration_limit (clip, 20);
CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 36);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 20, 33);
assert_set_max_duration (source1, 17);
assert_equals_int (limit_notify_count, 10);
_assert_duration_limit (clip, 11);
CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 11, 17);
CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 11, 26);
CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 11, 17);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 11, 25);
CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 11, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 11, 33);
assert_set_max_duration (source1, 30);
assert_set_duration (clip, 20);
assert_equals_int (limit_notify_count, 11);
_assert_duration_limit (clip, 20);
CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 30);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 20, 33);
assert_set_max_duration (clip, 17);
assert_equals_int (limit_notify_count, 12);
_assert_duration_limit (clip, 11);
CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 11, 17);
CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 11, 17);
CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 11, 17);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 11, 25);
CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 11, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 11, 33);
assert_set_max_duration (clip, 26);
assert_set_duration (clip, 20);
assert_equals_int (limit_notify_count, 13);
_assert_duration_limit (clip, 20);
CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 20, 33);
/* non-core */
assert_fail_set_max_duration (effect0, 10);
assert_fail_set_max_duration (effect2, 17);
assert_equals_int (limit_notify_count, 13);
_assert_duration_limit (clip, 20);
CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 20, 33);
assert_set_max_duration (effect2, 18);
assert_equals_int (limit_notify_count, 14);
_assert_duration_limit (clip, 11);
CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 11, 26);
CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 11, 26);
CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 11, 26);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 11, 25);
CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 11, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 11, 18);
assert_set_max_duration (effect2, 33);
assert_set_duration (clip, 20);
assert_equals_int (limit_notify_count, 15);
_assert_duration_limit (clip, 20);
CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 20, 33);
/* test setting active */
_assert_active (effect2, TRUE);
_assert_set_active (effect2, FALSE);
assert_set_max_duration (effect2, 17);
assert_equals_int (limit_notify_count, 15);
_assert_duration_limit (clip, 20);
CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 20, 17);
fail_if (ges_track_element_set_active (effect2, TRUE));
assert_equals_int (limit_notify_count, 15);
_assert_duration_limit (clip, 20);
CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 20, 17);
assert_set_inpoint (effect2, 6);
_assert_set_active (effect2, TRUE);
assert_equals_int (limit_notify_count, 16);
_assert_duration_limit (clip, 11);
CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 11, 26);
CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 11, 26);
CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 11, 26);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 11, 25);
CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 11, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (effect2, 10, 6, 11, 17);
/* make source0 in-active */
_assert_active (source0, TRUE);
_assert_set_active (source0, FALSE);
_assert_active (source0, FALSE);
_assert_active (effect0, FALSE);
_assert_active (effect1, FALSE);
_assert_active (effect2, FALSE);
assert_set_duration (source0, 20);
assert_equals_int (limit_notify_count, 17);
_assert_duration_limit (clip, 20);
CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (effect2, 10, 6, 20, 17);
/* lower duration-limit for source for when it becomes active */
assert_set_max_duration (source0, 16);
assert_equals_int (limit_notify_count, 17);
_assert_duration_limit (clip, 20);
CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 16);
CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 16);
CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (effect2, 10, 6, 20, 17);
/* cannot make the source active */
fail_if (ges_track_element_set_active (source0, TRUE));
assert_equals_int (limit_notify_count, 17);
_assert_duration_limit (clip, 20);
CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 16);
CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 16);
CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (effect2, 10, 6, 20, 17);
/* nor can we make the effects active because this would activate the
* source */
fail_if (ges_track_element_set_active (effect0, TRUE));
fail_if (ges_track_element_set_active (effect1, TRUE));
fail_if (ges_track_element_set_active (effect2, TRUE));
assert_equals_int (limit_notify_count, 17);
_assert_duration_limit (clip, 20);
CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 16);
CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 16);
CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 26);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (effect2, 10, 6, 20, 17);
/* allow it to just succeed */
assert_set_inpoint (source0, 5);
assert_equals_int (limit_notify_count, 18);
_assert_duration_limit (clip, 21);
CHECK_OBJECT_PROPS_MAX (clip, 10, 5, 20, 16);
CHECK_OBJECT_PROPS_MAX (source0, 10, 5, 20, 16);
CHECK_OBJECT_PROPS_MAX (source1, 10, 5, 20, 26);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (effect2, 10, 6, 20, 17);
_assert_set_active (effect1, TRUE);
assert_equals_int (limit_notify_count, 19);
_assert_duration_limit (clip, 11);
CHECK_OBJECT_PROPS_MAX (clip, 10, 5, 11, 16);
CHECK_OBJECT_PROPS_MAX (source0, 10, 5, 11, 16);
CHECK_OBJECT_PROPS_MAX (source1, 10, 5, 11, 26);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 11, 25);
CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 11, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (effect2, 10, 6, 11, 17);
gst_object_unref (timeline);
ges_deinit ();
}
GST_END_TEST;
GST_START_TEST (test_children_properties_contain)
{
GESTimeline *timeline;
@ -3670,6 +4213,7 @@ ges_suite (void)
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_can_set_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,25 +106,43 @@ 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_timeline_element_set(obj, prop, val) \
fail_unless (ges_timeline_element_set_ ## prop ( \
GES_TIMELINE_ELEMENT (obj), val), "Could not set the " # prop \
" of " #obj "(%s) to " #val, GES_TIMELINE_ELEMENT_NAME (obj))
#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 __fail_timeline_element_set(obj, prop, val) \
fail_if (ges_timeline_element_set_ ## prop ( \
GES_TIMELINE_ELEMENT (obj), val), "Setting the " # prop \
" of " #obj "(%s) to " #val " did not fail as expected", \
GES_TIMELINE_ELEMENT_NAME (obj))
#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_set_start(obj, val) \
__assert_timeline_element_set (obj, start, val)
#define assert_set_duration(obj, val) \
__assert_timeline_element_set (obj, duration, val)
#define assert_set_inpoint(obj, val) \
__assert_timeline_element_set (obj, inpoint, val)
#define assert_set_max_duration(obj, val) \
__assert_timeline_element_set (obj, max_duration, val)
#define assert_fail_set_start(obj, val) \
__fail_timeline_element_set (obj, start, val)
#define assert_fail_set_duration(obj, val) \
__fail_timeline_element_set (obj, duration, val)
#define assert_fail_set_inpoint(obj, val) \
__fail_timeline_element_set (obj, inpoint, val)
#define assert_fail_set_max_duration(obj, val) \
__fail_timeline_element_set (obj, max_duration, val)
#define assert_num_in_track(track, val) \
{ \