clip: re-handle child in-point and max-duration

The in-point of a clip is kept in sync with its core children, unless they
have no has-internal-source.

The max-duration is defined as the minimum max-duration amongst the
clip's core children. If it is set to a new value, this sets the
max-duration of its core children to the same value if they have
has-internal-source set as TRUE.

Non-core children (such as effects on a source clip) do not influence
these values.

As part of this, we no longer track in-point in GESContainer. Unlike start
and duration, the in-point of a timeline element does not refer to its
extent in the timeline. As such, it has little meaning for most
collections of timeline-elements, in particular GESGroups. As such, there
is no generic way to relate the in-point of a container to its children.
This commit is contained in:
Henry Wilkes 2020-03-10 11:38:58 +00:00
parent 7725e48a80
commit cd9cba55c0
9 changed files with 600 additions and 119 deletions

View file

@ -53,7 +53,12 @@
*
* The #GESTimelineElement:in-point of the clip will control the
* #GESTimelineElement:in-point of these core elements to be the same
* value.
* value if their #GESTrackElement:has-internal-source is set to %TRUE.
* The #GESTimelineElement:max-duration of the clip is the minimum
* #GESTimelineElement:max-duration of its children. If you set its value
* to anything other than its current value, this will also set the
* #GESTimelineElement:max-duration of all its core children to the same
* value if their #GESTrackElement:has-internal-source is set to %TRUE.
*
* ## Effects
*
@ -100,6 +105,10 @@ struct _GESClipPrivate
gboolean prevent_priority_offset_update;
gboolean prevent_resort;
gboolean updating_max_duration;
gboolean prevent_max_duration_update;
gboolean setting_inpoint;
/* The formats supported by this Clip */
GESTrackType supportedformats;
};
@ -178,6 +187,81 @@ _child_priority_changed_cb (GESTimelineElement * child,
}
}
static void
_child_inpoint_changed_cb (GESTimelineElement * child, GParamSpec * pspec,
GESContainer * container)
{
if (GES_CLIP (container)->priv->setting_inpoint)
return;
/* ignore non-core */
if (!ELEMENT_FLAG_IS_SET (child, GES_TRACK_ELEMENT_IS_CORE))
return;
/* 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 (!ges_track_element_has_internal_source (GES_TRACK_ELEMENT (child)))
return;
/* 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);
}
/* called when a child is added, removed or their max-duration changes */
static void
_update_max_duration (GESContainer * container)
{
GList *tmp;
GstClockTime min = GST_CLOCK_TIME_NONE;
GESClipPrivate *priv = GES_CLIP (container)->priv;
if (priv->prevent_max_duration_update)
return;
for (tmp = container->children; tmp; tmp = tmp->next) {
GESTimelineElement *child = tmp->data;
if (ELEMENT_FLAG_IS_SET (child, GES_TRACK_ELEMENT_IS_CORE)
&& GST_CLOCK_TIME_IS_VALID (child->maxduration))
min = GST_CLOCK_TIME_IS_VALID (min) ? MIN (min, child->maxduration) :
child->maxduration;
}
priv->updating_max_duration = TRUE;
ges_timeline_element_set_max_duration (GES_TIMELINE_ELEMENT (container), min);
priv->updating_max_duration = FALSE;
}
static void
_child_max_duration_changed_cb (GESTimelineElement * child,
GParamSpec * pspec, GESContainer * container)
{
/* ignore non-core */
if (!ELEMENT_FLAG_IS_SET (child, GES_TRACK_ELEMENT_IS_CORE))
return;
_update_max_duration (container);
}
static void
_child_has_internal_source_changed_cb (GESTimelineElement * child,
GParamSpec * pspec, GESContainer * container)
{
/* ignore non-core */
if (!ELEMENT_FLAG_IS_SET (child, GES_TRACK_ELEMENT_IS_CORE))
return;
/* if the track element is now registered to have no internal content,
* we don't have to do anything */
if (!ges_track_element_has_internal_source (GES_TRACK_ELEMENT (child)))
return;
/* otherwise, we need to make its in-point match ours */
_set_inpoint0 (child, _INPOINT (container));
}
/*****************************************************
* *
* GESTimelineElement virtual methods implementation *
@ -211,22 +295,18 @@ _set_start (GESTimelineElement * element, GstClockTime start)
static gboolean
_set_inpoint (GESTimelineElement * element, GstClockTime inpoint)
{
GList *tmp, *children;
GESContainer *container = GES_CONTAINER (element);
GList *tmp;
GESClipPrivate *priv = GES_CLIP (element)->priv;
children = ges_container_get_children (container, FALSE);
container->children_control_mode = GES_CHILDREN_IGNORE_NOTIFIES;
for (tmp = children; tmp; tmp = g_list_next (tmp)) {
GESTimelineElement *child = (GESTimelineElement *) tmp->data;
priv->setting_inpoint = TRUE;
for (tmp = GES_CONTAINER_CHILDREN (element); tmp; tmp = tmp->next) {
GESTimelineElement *child = tmp->data;
/* FIXME: we should allow the inpoint to be different for children
* that are *not* the core track elements of the clip. E.g. if we
* add a GESEffect to a clip, we should not be editing its inpoint */
if (child != container->initiated_move)
if (ELEMENT_FLAG_IS_SET (child, GES_TRACK_ELEMENT_IS_CORE)
&& ges_track_element_has_internal_source (GES_TRACK_ELEMENT (child)))
_set_inpoint0 (child, inpoint);
}
container->children_control_mode = GES_CHILDREN_UPDATE;
g_list_free_full (children, gst_object_unref);
priv->setting_inpoint = FALSE;
return TRUE;
}
@ -260,10 +340,46 @@ static gboolean
_set_max_duration (GESTimelineElement * element, GstClockTime maxduration)
{
GList *tmp;
GESClipPrivate *priv = GES_CLIP (element)->priv;
GstClockTime new_min = GST_CLOCK_TIME_NONE;
for (tmp = GES_CONTAINER (element)->children; tmp; tmp = g_list_next (tmp))
ges_timeline_element_set_max_duration (GES_TIMELINE_ELEMENT (tmp->data),
maxduration);
/* if we are setting based on a change in the minimum */
if (priv->updating_max_duration)
return TRUE;
/* else, we set every core child to have the same max duration */
priv->prevent_max_duration_update = TRUE;
for (tmp = GES_CONTAINER_CHILDREN (element); tmp; tmp = tmp->next) {
GESTimelineElement *child = tmp->data;
if (ELEMENT_FLAG_IS_SET (child, GES_TRACK_ELEMENT_IS_CORE)
&& ges_track_element_has_internal_source (GES_TRACK_ELEMENT (child))) {
ges_timeline_element_set_max_duration (child, 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;
}
}
}
priv->prevent_max_duration_update = FALSE;
if (new_min != maxduration) {
if (GST_CLOCK_TIME_IS_VALID (new_min))
GST_WARNING_OBJECT (element, "Failed to set the max-duration of the "
"clip to %" GST_TIME_FORMAT " because it was not possible to "
"match this with the actual minimum of %" GST_TIME_FORMAT,
GST_TIME_ARGS (maxduration), GST_TIME_ARGS (new_min));
else
GST_WARNING_OBJECT (element, "Failed to set the max-duration of the "
"clip to %" GST_TIME_FORMAT " because it has no core children "
"whose max-duration could be set to anything other than "
"GST_CLOCK_TIME_NONE", GST_TIME_ARGS (maxduration));
priv->updating_max_duration = TRUE;
ges_timeline_element_set_max_duration (element, new_min);
priv->updating_max_duration = FALSE;
return FALSE;
}
return TRUE;
}
@ -384,7 +500,8 @@ _add_child (GESContainer * container, GESTimelineElement * element)
/* Set the core element to have the same in-point, which we don't
* apply to effects */
_set_inpoint0 (element, GES_TIMELINE_ELEMENT_INPOINT (container));
if (ges_track_element_has_internal_source (GES_TRACK_ELEMENT (element)))
_set_inpoint0 (element, _INPOINT (container));
} else if (GES_CLIP_CLASS_CAN_ADD_EFFECTS (klass)
&& GES_IS_BASE_EFFECT (element)) {
GList *tmp;
@ -471,11 +588,17 @@ _remove_child (GESContainer * container, GESTimelineElement * element)
static void
_child_added (GESContainer * container, GESTimelineElement * element)
{
/* FIXME: adjust the max-duration according to the new child */
g_signal_connect (G_OBJECT (element), "notify::priority",
G_CALLBACK (_child_priority_changed_cb), container);
g_signal_connect (G_OBJECT (element), "notify::in-point",
G_CALLBACK (_child_inpoint_changed_cb), container);
g_signal_connect (G_OBJECT (element), "notify::max-duration",
G_CALLBACK (_child_max_duration_changed_cb), container);
g_signal_connect (G_OBJECT (element), "notify::has-internal-source",
G_CALLBACK (_child_has_internal_source_changed_cb), container);
_child_priority_changed_cb (element, NULL, container);
_update_max_duration (container);
}
static void
@ -483,6 +606,14 @@ _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);
_update_max_duration (container);
}
static void
@ -928,9 +1059,15 @@ ges_clip_class_init (GESClipClass * klass)
static void
ges_clip_init (GESClip * self)
{
self->priv = ges_clip_get_instance_private (self);
self->priv->layer = NULL;
self->priv->nb_effects = 0;
GESClipPrivate *priv;
priv = self->priv = ges_clip_get_instance_private (self);
priv->layer = NULL;
priv->nb_effects = 0;
priv->prevent_priority_offset_update = FALSE;
priv->prevent_resort = FALSE;
priv->updating_max_duration = FALSE;
priv->prevent_max_duration_update = FALSE;
priv->setting_inpoint = FALSE;
}
/**
@ -1061,12 +1198,6 @@ ges_clip_create_track_elements (GESClip * clip, GESTrackType type)
for (tmp = track_elements; tmp; tmp = tmp->next) {
GESTimelineElement *elem = tmp->data;
ELEMENT_SET_FLAG (elem, GES_TRACK_ELEMENT_IS_CORE);
/* FIXME: we should set the max duration of the clip based on the
* max duration of the child. Not the other way around. */
if (GST_CLOCK_TIME_IS_VALID (GES_TIMELINE_ELEMENT_MAX_DURATION (clip)))
ges_timeline_element_set_max_duration (GES_TIMELINE_ELEMENT (elem),
GES_TIMELINE_ELEMENT_MAX_DURATION (clip));
}
result = g_list_concat (track_elements, result);
}
@ -1583,6 +1714,9 @@ ges_clip_split (GESClip * clip, guint64 position)
continue;
}
/* FIXME: in-point for non-core track elements should be shifted by
* the split (adding them to the new clip will not set their in-point)
* Handle this once generic time effects are supported in splitting */
ges_container_add (GES_CONTAINER (new_object),
GES_TIMELINE_ELEMENT (new_trackelement));
ges_track_element_copy_properties (GES_TIMELINE_ELEMENT (trackelement),

View file

@ -58,12 +58,10 @@ typedef struct
GstClockTime start_offset;
GstClockTime duration_offset;
GstClockTime inpoint_offset;
gint32 priority_offset;
gulong start_notifyid;
gulong duration_notifyid;
gulong inpoint_notifyid;
gulong child_property_added_notifyid;
gulong child_property_removed_notifyid;
} ChildMapping;
@ -120,8 +118,6 @@ _free_mapping (ChildMapping * mapping)
g_signal_handler_disconnect (child, mapping->start_notifyid);
if (mapping->duration_notifyid)
g_signal_handler_disconnect (child, mapping->duration_notifyid);
if (mapping->inpoint_notifyid)
g_signal_handler_disconnect (child, mapping->inpoint_notifyid);
if (mapping->child_property_added_notifyid)
g_signal_handler_disconnect (child, mapping->child_property_added_notifyid);
if (mapping->child_property_removed_notifyid)
@ -186,22 +182,6 @@ _set_start (GESTimelineElement * element, GstClockTime start)
return TRUE;
}
static gboolean
_set_inpoint (GESTimelineElement * element, GstClockTime inpoint)
{
GList *tmp;
GESContainer *container = GES_CONTAINER (element);
for (tmp = container->children; tmp; tmp = g_list_next (tmp)) {
GESTimelineElement *child = (GESTimelineElement *) tmp->data;
ChildMapping *map = g_hash_table_lookup (container->priv->mappings, child);
map->inpoint_offset = inpoint - _INPOINT (child);
}
return TRUE;
}
static gboolean
_set_duration (GESTimelineElement * element, GstClockTime duration)
{
@ -369,7 +349,6 @@ _deep_copy (GESTimelineElement * element, GESTimelineElement * copy)
tmp->data));
map->child = ges_timeline_element_copy (tmp->data, TRUE);
map->start_notifyid = 0;
map->inpoint_notifyid = 0;
map->duration_notifyid = 0;
ccopy->priv->copied_children = g_list_prepend (ccopy->priv->copied_children,
@ -532,7 +511,6 @@ ges_container_class_init (GESContainerClass * klass)
element_class->set_start = _set_start;
element_class->set_duration = _set_duration;
element_class->set_inpoint = _set_inpoint;
element_class->lookup_child = _lookup_child;
element_class->get_track_types = _get_track_types;
element_class->paste = _paste;
@ -625,34 +603,6 @@ _child_start_changed_cb (GESTimelineElement * child,
container->children_control_mode = pmode;
}
static void
_child_inpoint_changed_cb (GESTimelineElement * child,
GParamSpec * arg G_GNUC_UNUSED, GESContainer * container)
{
ChildMapping *map;
GESContainerPrivate *priv = container->priv;
GESTimelineElement *element = GES_TIMELINE_ELEMENT (container);
if (container->children_control_mode == GES_CHILDREN_IGNORE_NOTIFIES)
return;
map = g_hash_table_lookup (priv->mappings, child);
g_assert (map);
if (container->children_control_mode == GES_CHILDREN_UPDATE_OFFSETS
|| ELEMENT_FLAG_IS_SET (child, GES_TIMELINE_ELEMENT_SET_SIMPLE)) {
map->inpoint_offset = _INPOINT (container) - _INPOINT (child);
return;
}
/* We update all the children calling our set_inpoint method */
container->initiated_move = child;
_set_inpoint0 (element, _INPOINT (child) + map->inpoint_offset);
container->initiated_move = NULL;
}
static void
_child_duration_changed_cb (GESTimelineElement * child,
GParamSpec * arg G_GNUC_UNUSED, GESContainer * container)
@ -838,7 +788,6 @@ ges_container_add (GESContainer * container, GESTimelineElement * child)
mapping->child = gst_object_ref (child);
mapping->start_offset = _START (container) - _START (child);
mapping->duration_offset = _DURATION (container) - _DURATION (child);
mapping->inpoint_offset = _INPOINT (container) - _INPOINT (child);
g_hash_table_insert (priv->mappings, child, mapping);
@ -853,9 +802,6 @@ ges_container_add (GESContainer * container, GESTimelineElement * child)
mapping->duration_notifyid =
g_signal_connect (G_OBJECT (child), "notify::duration",
G_CALLBACK (_child_duration_changed_cb), container);
mapping->inpoint_notifyid =
g_signal_connect (G_OBJECT (child), "notify::in-point",
G_CALLBACK (_child_inpoint_changed_cb), container);
if (ges_timeline_element_set_parent (child, GES_TIMELINE_ELEMENT (container))
== FALSE) {

View file

@ -303,6 +303,9 @@ _child_removed (GESContainer * container, GESTimelineElement * element)
priv->video_transitions = g_slist_remove (priv->video_transitions, element);
gst_object_unref (element);
}
/* call parent method */
GES_CONTAINER_CLASS (ges_transition_clip_parent_class)->child_removed
(container, element);
}
static void
@ -322,6 +325,9 @@ _child_added (GESContainer * container, GESTimelineElement * element)
ges_timeline_element_add_child_property (GES_TIMELINE_ELEMENT (container),
g_object_class_find_property (eklass, "border"), G_OBJECT (element));
}
/* call parent method */
GES_CONTAINER_CLASS (ges_transition_clip_parent_class)->child_added
(container, element);
}
static GESTrackElement *

View file

@ -302,8 +302,6 @@ extractable_set_asset (GESExtractable * self, GESAsset * asset)
_set_duration0 (GES_TIMELINE_ELEMENT (uriclip),
ges_uri_clip_asset_get_duration (uri_clip_asset));
ges_timeline_element_set_max_duration (GES_TIMELINE_ELEMENT (uriclip),
ges_uri_clip_asset_get_max_duration (uri_clip_asset));
ges_uri_clip_set_is_image (uriclip,
ges_uri_clip_asset_is_image (uri_clip_asset));
@ -421,6 +419,7 @@ uri_clip_set_max_duration (GESTimelineElement * element,
{
if (_DURATION (element) == GST_CLOCK_TIME_NONE || _DURATION (element) == 0)
/* If we don't have a valid duration, use the max duration */
/* FIXME: don't do this when we have time effects */
_set_duration0 (element, maxduration - _INPOINT (element));
return
@ -488,17 +487,28 @@ ges_uri_clip_create_track_elements (GESClip * clip, GESTrackType type)
{
GList *res = NULL;
const GList *tmp, *stream_assets;
GESAsset *asset = GES_TIMELINE_ELEMENT (clip)->asset;
GESUriClipAsset *uri_asset;
GstClockTime max_duration;
g_return_val_if_fail (GES_TIMELINE_ELEMENT (clip)->asset, NULL);
g_return_val_if_fail (asset, NULL);
uri_asset = GES_URI_CLIP_ASSET (asset);
max_duration = ges_uri_clip_asset_get_max_duration (uri_asset);
stream_assets = ges_uri_clip_asset_get_stream_assets (uri_asset);
stream_assets =
ges_uri_clip_asset_get_stream_assets (GES_URI_CLIP_ASSET
(GES_TIMELINE_ELEMENT (clip)->asset));
for (tmp = stream_assets; tmp; tmp = tmp->next) {
GESTrackElementAsset *asset = GES_TRACK_ELEMENT_ASSET (tmp->data);
GESTrackElementAsset *element_asset = GES_TRACK_ELEMENT_ASSET (tmp->data);
if (ges_track_element_asset_get_track_type (asset) == type)
res = g_list_prepend (res, ges_asset_extract (GES_ASSET (asset), NULL));
if (ges_track_element_asset_get_track_type (element_asset) == type) {
GESTrackElement *element =
GES_TRACK_ELEMENT (ges_asset_extract (GES_ASSET (element_asset),
NULL));
ges_timeline_element_set_max_duration (GES_TIMELINE_ELEMENT (element),
max_duration);
res = g_list_prepend (res, element);
}
}
return res;

View file

@ -792,19 +792,8 @@ _count_cb (GObject * obj, GParamSpec * pspec, gint * count)
}
static void
_test_children_time_setting_on_clip (GESClip * clip, GESTimelineElement * child)
_test_children_time_setting_on_clip (GESClip * clip, GESTrackElement * child)
{
/* FIXME: Don't necessarily want to change the inpoint of all the
* children if the clip inpoint changes. Really, we would only expect
* the inpoint to change for the source elements within a clip.
* Setting the inpoint of an operation may be irrelevant, and for
* operations where it *is* relevant, we would ideally want it to be
* independent from the source element's inpoint (unlike the start and
* duration values).
* However, this is the current behaviour, but if this is changed this
* test should be changed to only check that source elements have
* their inpoint changed, and operation elements have their inpoint
* unchanged */
_assert_children_time_setter (clip, child, "in-point",
ges_timeline_element_set_inpoint, 11, 101);
_assert_children_time_setter (clip, child, "in-point",
@ -842,14 +831,16 @@ GST_START_TEST (test_children_time_setters)
GESClip *clip = clips[i];
GESContainer *group = GES_CONTAINER (ges_group_new ());
GList *children;
GESTimelineElement *child;
GESTrackElement *child;
/* no children */
_test_children_time_setting_on_clip (clip, NULL);
/* child in timeline */
fail_unless (ges_layer_add_clip (layer, clip));
children = GES_CONTAINER_CHILDREN (clip);
fail_unless (children);
child = GES_TIMELINE_ELEMENT (children->data);
child = GES_TRACK_ELEMENT (children->data);
/* make sure the child can have its in-point set */
ges_track_element_set_has_internal_source (child, TRUE);
_test_children_time_setting_on_clip (clip, child);
/* clip in a group */
ges_container_add (group, GES_TIMELINE_ELEMENT (clip));
@ -861,7 +852,8 @@ GST_START_TEST (test_children_time_setters)
fail_unless (ges_layer_remove_clip (layer, clip));
children = GES_CONTAINER_CHILDREN (clip);
fail_unless (children);
child = GES_TIMELINE_ELEMENT (children->data);
child = GES_TRACK_ELEMENT (children->data);
ges_track_element_set_has_internal_source (child, TRUE);
_test_children_time_setting_on_clip (clip, child);
gst_object_unref (clip);
}
@ -926,6 +918,376 @@ GST_START_TEST (test_can_add_effect)
GST_END_TEST;
GST_START_TEST (test_children_inpoint)
{
GESTimeline *timeline;
GESLayer *layer;
GESTimelineElement *clip, *child0, *child1, *effect;
GList *children;
ges_init ();
timeline = ges_timeline_new_audio_video ();
fail_unless (timeline);
layer = ges_timeline_append_layer (timeline);
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));
CHECK_OBJECT_PROPS (clip, 5, 30, 20);
fail_unless (ges_layer_add_clip (layer, GES_CLIP (clip)));
/* clip now has children */
children = GES_CONTAINER_CHILDREN (clip);
fail_unless (children);
child0 = children->data;
fail_unless (children->next);
child1 = children->next->data;
fail_unless (children->next->next == NULL);
fail_unless (ges_track_element_has_internal_source (GES_TRACK_ELEMENT
(child0)));
fail_unless (ges_track_element_has_internal_source (GES_TRACK_ELEMENT
(child1)));
CHECK_OBJECT_PROPS (clip, 5, 30, 20);
CHECK_OBJECT_PROPS (child0, 5, 30, 20);
CHECK_OBJECT_PROPS (child1, 5, 30, 20);
/* add a non-core element */
effect = GES_TIMELINE_ELEMENT (ges_effect_new ("agingtv"));
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));
/* adding the effect will change its start and duration, but not its
* in-point */
fail_unless (ges_container_add (GES_CONTAINER (clip), effect));
CHECK_OBJECT_PROPS (clip, 5, 30, 20);
CHECK_OBJECT_PROPS (child0, 5, 30, 20);
CHECK_OBJECT_PROPS (child1, 5, 30, 20);
CHECK_OBJECT_PROPS (effect, 5, 67, 20);
/* register child0 as having no internal source, which means its
* in-point will be set to 0 */
ges_track_element_set_has_internal_source (GES_TRACK_ELEMENT (child0), FALSE);
CHECK_OBJECT_PROPS (clip, 5, 30, 20);
CHECK_OBJECT_PROPS (child0, 5, 0, 20);
CHECK_OBJECT_PROPS (child1, 5, 30, 20);
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));
CHECK_OBJECT_PROPS (clip, 5, 30, 20);
CHECK_OBJECT_PROPS (child0, 5, 0, 20);
CHECK_OBJECT_PROPS (child1, 5, 30, 20);
CHECK_OBJECT_PROPS (effect, 5, 67, 20);
/* 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));
CHECK_OBJECT_PROPS (clip, 5, 50, 20);
/* child with no internal source not changed */
CHECK_OBJECT_PROPS (child0, 5, 0, 20);
CHECK_OBJECT_PROPS (child1, 5, 50, 20);
/* non-core no changed */
CHECK_OBJECT_PROPS (effect, 5, 67, 20);
/* setting back to having internal source will put in sync with the
* in-point of the clip */
ges_track_element_set_has_internal_source (GES_TRACK_ELEMENT (child0), TRUE);
CHECK_OBJECT_PROPS (clip, 5, 50, 20);
CHECK_OBJECT_PROPS (child0, 5, 50, 20);
CHECK_OBJECT_PROPS (child1, 5, 50, 20);
CHECK_OBJECT_PROPS (effect, 5, 67, 20);
fail_unless (ges_timeline_element_set_inpoint (child0, 40));
CHECK_OBJECT_PROPS (clip, 5, 40, 20);
CHECK_OBJECT_PROPS (child0, 5, 40, 20);
CHECK_OBJECT_PROPS (child1, 5, 40, 20);
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));
CHECK_OBJECT_PROPS (clip, 5, 40, 20);
CHECK_OBJECT_PROPS (child0, 5, 40, 20);
CHECK_OBJECT_PROPS (child1, 5, 40, 20);
CHECK_OBJECT_PROPS (effect, 5, 77, 20);
gst_object_unref (timeline);
ges_deinit ();
}
GST_END_TEST;
GST_START_TEST (test_children_max_duration)
{
GESTimeline *timeline;
GESLayer *layer;
gchar *uri;
GESTimelineElement *clips[] = { NULL, NULL };
GESTimelineElement *child0, *child1, *effect;
guint i;
GstClockTime max_duration, new_max;
GList *children;
ges_init ();
timeline = ges_timeline_new_audio_video ();
fail_unless (timeline);
layer = ges_timeline_append_layer (timeline);
uri = ges_test_get_audio_video_uri ();
clips[0] = GES_TIMELINE_ELEMENT (ges_uri_clip_new (uri));
fail_unless (clips[0]);
g_free (uri);
clips[1] = GES_TIMELINE_ELEMENT (ges_test_clip_new ());
for (i = 0; i < G_N_ELEMENTS (clips); i++) {
GESTimelineElement *clip = clips[i];
fail_unless (_MAX_DURATION (clip) == GST_CLOCK_TIME_NONE);
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));
/* can not set the max duration when we have no children */
fail_if (ges_timeline_element_set_max_duration (clip, 150));
CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, GST_CLOCK_TIME_NONE);
fail_unless (ges_layer_add_clip (layer, GES_CLIP (clip)));
/* clip now has children */
children = GES_CONTAINER_CHILDREN (clip);
fail_unless (children);
child0 = children->data;
fail_unless (children->next);
child1 = children->next->data;
fail_unless (children->next->next == NULL);
fail_unless (ges_track_element_has_internal_source (GES_TRACK_ELEMENT
(child0)));
fail_unless (ges_track_element_has_internal_source (GES_TRACK_ELEMENT
(child1)));
if (GES_IS_URI_CLIP (clip)) {
/* uri clip children should be created with a max-duration set */
/* each created child has the same max-duration */
max_duration = _MAX_DURATION (child0);
fail_unless (max_duration != GST_CLOCK_TIME_NONE);
new_max = max_duration;
} else {
max_duration = GST_CLOCK_TIME_NONE;
/* need a valid clock time that is not too large */
new_max = 500;
}
/* added children do not change the clip's max-duration, but will
* instead set it to the minimum value of its children */
CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, max_duration);
CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, max_duration);
CHECK_OBJECT_PROPS_MAX (child1, 5, 30, 20, max_duration);
/* add a non-core element */
effect = GES_TIMELINE_ELEMENT (ges_effect_new ("agingtv"));
fail_if (ges_track_element_has_internal_source (GES_TRACK_ELEMENT
(effect)));
/* 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, 1));
/* 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));
CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, max_duration);
CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, max_duration);
CHECK_OBJECT_PROPS_MAX (child1, 5, 30, 20, max_duration);
CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 1);
/* 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));
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, 1);
fail_unless (ges_timeline_element_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, 1);
fail_unless (ges_timeline_element_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);
CHECK_OBJECT_PROPS_MAX (child1, 5, 30, 20, new_max - 2);
CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 1);
/* non-core has no effect */
fail_unless (ges_timeline_element_set_max_duration (effect, new_max - 4));
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 - 4);
fail_unless (ges_timeline_element_set_max_duration (effect, 1));
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, 1);
/* setting on the clip will set all the core children to the same
* value */
fail_unless (ges_timeline_element_set_max_duration (clip, 180));
CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, 180);
CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, 180);
CHECK_OBJECT_PROPS_MAX (child1, 5, 30, 20, 180);
CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 1);
/* register child0 as having no internal source, which means its
* in-point will be set to 0 and max-duration set to
* GST_CLOCK_TIME_NONE */
ges_track_element_set_has_internal_source (GES_TRACK_ELEMENT (child0),
FALSE);
CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, 180);
CHECK_OBJECT_PROPS_MAX (child0, 5, 0, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (child1, 5, 30, 20, 180);
CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 1);
/* should not be able to set the max-duration to a valid time */
fail_if (ges_timeline_element_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);
CHECK_OBJECT_PROPS_MAX (child1, 5, 30, 20, 180);
CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 1);
/* same with child1 */
/* clock time of the clip should now be GST_CLOCK_TIME_NONE */
ges_track_element_set_has_internal_source (GES_TRACK_ELEMENT (child1),
FALSE);
CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (child0, 5, 0, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (child1, 5, 0, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 1);
/* 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));
CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (child0, 5, 0, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (child1, 5, 0, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 1);
/* setting back to having an internal source will not immediately
* change the max-duration (unlike in-point) */
ges_track_element_set_has_internal_source (GES_TRACK_ELEMENT (child0),
TRUE);
CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (child1, 5, 0, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 1);
/* can now set the max-duration, which will effect the clip */
fail_unless (ges_timeline_element_set_max_duration (child0, 140));
CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, 140);
CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, 140);
CHECK_OBJECT_PROPS_MAX (child1, 5, 0, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 1);
ges_track_element_set_has_internal_source (GES_TRACK_ELEMENT (child1),
TRUE);
CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, 140);
CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, 140);
CHECK_OBJECT_PROPS_MAX (child1, 5, 30, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 1);
fail_unless (ges_timeline_element_set_max_duration (child1, 130));
CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, 130);
CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, 140);
CHECK_OBJECT_PROPS_MAX (child1, 5, 30, 20, 130);
CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 1);
/* removing a child may change the max_duration of the clip */
gst_object_ref (child0);
gst_object_ref (child1);
gst_object_ref (effect);
/* removing non-core does nothing */
fail_unless (ges_container_remove (GES_CONTAINER (clip), effect));
CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, 130);
CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, 140);
CHECK_OBJECT_PROPS_MAX (child1, 5, 30, 20, 130);
CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 1);
/* new minimum max-duration for the clip when we remove child1 */
fail_unless (ges_container_remove (GES_CONTAINER (clip), child1));
CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, 140);
CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, 140);
CHECK_OBJECT_PROPS_MAX (child1, 5, 30, 20, 130);
CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 1);
/* 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));
CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, GST_CLOCK_TIME_NONE);
CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, 140);
CHECK_OBJECT_PROPS_MAX (child1, 5, 30, 20, 130);
CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 1);
fail_unless (ges_layer_remove_clip (layer, GES_CLIP (clip)));
gst_object_unref (child0);
gst_object_unref (child1);
gst_object_unref (effect);
}
gst_object_unref (timeline);
ges_deinit ();
}
GST_END_TEST;
GST_START_TEST (test_children_properties_contain)
{
GESTimeline *timeline;
@ -1444,6 +1806,8 @@ ges_suite (void)
tcase_add_test (tc_chain, test_effects_priorities);
tcase_add_test (tc_chain, test_children_time_setters);
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_children_properties_contain);
tcase_add_test (tc_chain, test_children_properties_change);
tcase_add_test (tc_chain, test_copy_paste_children_properties);

View file

@ -79,10 +79,11 @@ GST_START_TEST (test_overlay_properties)
/* Check that trackelement has the same properties */
assert_equals_uint64 (_START (trackelement), 42);
assert_equals_uint64 (_DURATION (trackelement), 51);
assert_equals_uint64 (_INPOINT (trackelement), 12);
/* in-point is 0 since it does not have has-internal-source */
assert_equals_uint64 (_INPOINT (trackelement), 0);
/* And let's also check that it propagated correctly to GNonLin */
nle_object_check (ges_track_element_get_nleobject (trackelement), 42, 51, 12,
nle_object_check (ges_track_element_get_nleobject (trackelement), 42, 51, 0,
51, MIN_NLE_PRIO + TRANSITIONS_HEIGHT, TRUE);
/* Change more properties, see if they propagate */
@ -94,11 +95,11 @@ GST_START_TEST (test_overlay_properties)
assert_equals_uint64 (_INPOINT (clip), 120);
assert_equals_uint64 (_START (trackelement), 420);
assert_equals_uint64 (_DURATION (trackelement), 510);
assert_equals_uint64 (_INPOINT (trackelement), 120);
assert_equals_uint64 (_INPOINT (trackelement), 0);
/* And let's also check that it propagated correctly to GNonLin */
nle_object_check (ges_track_element_get_nleobject (trackelement), 420, 510,
120, 510, MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 0, TRUE);
0, 510, MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 0, TRUE);
ges_container_remove (GES_CONTAINER (clip),
GES_TIMELINE_ELEMENT (trackelement));

View file

@ -88,6 +88,7 @@ G_STMT_START { \
#define _START(obj) GES_TIMELINE_ELEMENT_START (obj)
#define _DURATION(obj) GES_TIMELINE_ELEMENT_DURATION (obj)
#define _INPOINT(obj) GES_TIMELINE_ELEMENT_INPOINT (obj)
#define _MAX_DURATION(obj) GES_TIMELINE_ELEMENT_MAX_DURATION (obj)
#define _PRIORITY(obj) GES_TIMELINE_ELEMENT_PRIORITY (obj)
#ifndef _END
#define _END(obj) (_START(obj) + _DURATION(obj))
@ -99,6 +100,14 @@ G_STMT_START { \
fail_unless (_DURATION (obj) == duration, "%s duration is %" GST_TIME_FORMAT " != %" GST_TIME_FORMAT, GES_TIMELINE_ELEMENT_NAME(obj), GST_TIME_ARGS (_DURATION(obj)), GST_TIME_ARGS (duration));\
}
#define CHECK_OBJECT_PROPS_MAX(obj, start, inpoint, duration, max_duration) {\
CHECK_OBJECT_PROPS (obj, start, inpoint, duration); \
fail_unless (_MAX_DURATION(obj) == max_duration, "%s max-duration is " \
"%" GST_TIME_FORMAT " != %" GST_TIME_FORMAT, \
GES_TIMELINE_ELEMENT_NAME(obj), \
GST_TIME_ARGS (_MAX_DURATION(obj)), GST_TIME_ARGS (max_duration)); \
}
/* assert that the time property (start, duration or in-point) is the
* same as @cmp for the clip and all its children */
#define assert_clip_children_time_val(clip, property, cmp) \
@ -106,6 +115,7 @@ G_STMT_START { \
GList *tmp; \
GstClockTime read_val; \
gchar *name = GES_TIMELINE_ELEMENT (clip)->name; \
gboolean is_inpoint = (g_strcmp0 (property, "in-point") == 0); \
g_object_get (clip, property, &read_val, NULL); \
fail_unless (read_val == cmp, "The %s property for clip %s is %" \
GST_TIME_FORMAT ", rather than the expected value of %" \
@ -115,10 +125,16 @@ G_STMT_START { \
tmp = tmp->next) { \
GESTimelineElement *child = tmp->data; \
g_object_get (child, property, &read_val, NULL); \
fail_unless (read_val == cmp, "The %s property for the child %s " \
"of clip %s is %" GST_TIME_FORMAT ", rather than the expected " \
"value of %" GST_TIME_FORMAT, property, child->name, name, \
GST_TIME_ARGS (read_val), GST_TIME_ARGS (cmp)); \
if (!is_inpoint || ges_track_element_has_internal_source ( \
GES_TRACK_ELEMENT (child))) \
fail_unless (read_val == cmp, "The %s property for the child %s " \
"of clip %s is %" GST_TIME_FORMAT ", rather than the expected" \
" value of %" GST_TIME_FORMAT, property, child->name, name, \
GST_TIME_ARGS (read_val), GST_TIME_ARGS (cmp)); \
else \
fail_unless (read_val == 0, "The %s property for the child %s " \
"of clip %s is %" GST_TIME_FORMAT ", rather than 0", \
property, child->name, name, GST_TIME_ARGS (read_val)); \
} \
}

View file

@ -65,7 +65,10 @@ GST_START_TEST (test_title_source_properties)
"in-point", (guint64) 12, NULL);
assert_equals_uint64 (_START (clip), 42);
assert_equals_uint64 (_DURATION (clip), 51);
assert_equals_uint64 (_INPOINT (clip), 0);
/* the clip can have a non-zero in-point, but this won't affect any
* of the core children because they have their has-internal-source set
* to FALSE */
assert_equals_uint64 (_INPOINT (clip), 12);
ges_layer_add_clip (layer, GES_CLIP (clip));
ges_timeline_commit (timeline);
@ -91,7 +94,7 @@ GST_START_TEST (test_title_source_properties)
ges_timeline_commit (timeline);
assert_equals_uint64 (_START (clip), 420);
assert_equals_uint64 (_DURATION (clip), 510);
assert_equals_uint64 (_INPOINT (clip), 0);
assert_equals_uint64 (_INPOINT (clip), 120);
assert_equals_uint64 (_START (trackelement), 420);
assert_equals_uint64 (_DURATION (trackelement), 510);
assert_equals_uint64 (_INPOINT (trackelement), 0);

View file

@ -109,10 +109,11 @@ GST_START_TEST (test_transition_properties)
/* Check that trackelement has the same properties */
assert_equals_uint64 (_START (trackelement), 42);
assert_equals_uint64 (_DURATION (trackelement), 51);
assert_equals_uint64 (_INPOINT (trackelement), 12);
/* in-point is 0 since it does not have has-internal-source */
assert_equals_uint64 (_INPOINT (trackelement), 0);
/* And let's also check that it propagated correctly to GNonLin */
nle_object_check (ges_track_element_get_nleobject (trackelement), 42, 51, 12,
nle_object_check (ges_track_element_get_nleobject (trackelement), 42, 51, 0,
51, MIN_NLE_PRIO, TRUE);
/* Change more properties, see if they propagate */
@ -124,11 +125,11 @@ GST_START_TEST (test_transition_properties)
assert_equals_uint64 (_INPOINT (clip), 120);
assert_equals_uint64 (_START (trackelement), 420);
assert_equals_uint64 (_DURATION (trackelement), 510);
assert_equals_uint64 (_INPOINT (trackelement), 120);
assert_equals_uint64 (_INPOINT (trackelement), 0);
/* And let's also check that it propagated correctly to GNonLin */
nle_object_check (ges_track_element_get_nleobject (trackelement), 420, 510,
120, 510, MIN_NLE_PRIO + 0, TRUE);
0, 510, MIN_NLE_PRIO + 0, TRUE);
/* test changing vtype */
GST_DEBUG ("Setting to crossfade");