diff --git a/ges/ges-clip.c b/ges/ges-clip.c index 533ab22e7a..11b58ee63d 100644 --- a/ges/ges-clip.c +++ b/ges/ges-clip.c @@ -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), diff --git a/ges/ges-container.c b/ges/ges-container.c index d6b2487e60..cf093b2960 100644 --- a/ges/ges-container.c +++ b/ges/ges-container.c @@ -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) { diff --git a/ges/ges-transition-clip.c b/ges/ges-transition-clip.c index d0fb494f90..3da69e151e 100644 --- a/ges/ges-transition-clip.c +++ b/ges/ges-transition-clip.c @@ -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 * diff --git a/ges/ges-uri-clip.c b/ges/ges-uri-clip.c index 8ec8085715..de704d8c20 100644 --- a/ges/ges-uri-clip.c +++ b/ges/ges-uri-clip.c @@ -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; diff --git a/tests/check/ges/clip.c b/tests/check/ges/clip.c index 3a4f1ee856..0cc00ccb28 100644 --- a/tests/check/ges/clip.c +++ b/tests/check/ges/clip.c @@ -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); diff --git a/tests/check/ges/overlays.c b/tests/check/ges/overlays.c index a9b041b2d7..b169e41636 100644 --- a/tests/check/ges/overlays.c +++ b/tests/check/ges/overlays.c @@ -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)); diff --git a/tests/check/ges/test-utils.h b/tests/check/ges/test-utils.h index 3f426f4dec..d98b4fb51f 100644 --- a/tests/check/ges/test-utils.h +++ b/tests/check/ges/test-utils.h @@ -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)); \ } \ } diff --git a/tests/check/ges/titles.c b/tests/check/ges/titles.c index b9bf4a6e79..e5b19dfc6f 100644 --- a/tests/check/ges/titles.c +++ b/tests/check/ges/titles.c @@ -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); diff --git a/tests/check/ges/transition.c b/tests/check/ges/transition.c index 9c826d433c..16b2880be4 100644 --- a/tests/check/ges/transition.c +++ b/tests/check/ges/transition.c @@ -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");