diff --git a/ges/ges-base-effect-clip.c b/ges/ges-base-effect-clip.c index 4cf1e65be4..118832c53e 100644 --- a/ges/ges-base-effect-clip.c +++ b/ges/ges-base-effect-clip.c @@ -20,11 +20,21 @@ /** * SECTION: gesbaseeffectclip * @title: GESBaseEffectClip - * @short_description: An effect in a GESLayer + * @short_description: An effect in a #GESLayer * - * The effect will be applied on the sources that have lower priorities - * (higher number) between the inpoint and the end of it. + * #GESBaseEffectClip-s are clips whose core elements are + * #GESBaseEffect-s. + * + * ## Effects + * + * #GESBaseEffectClip-s can have **additional** #GESBaseEffect-s added as + * non-core elements. These additional effects are applied to the output + * of the core effects of the clip that they share a #GESTrack with. See + * #GESClip for how to add and move these effects from the clip. */ + +/* FIXME: properly handle the priority of the children. How should we sort + * the priority of effects when two #GESBaseEffectClip's overlap? */ #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -44,6 +54,7 @@ G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GESBaseEffectClip, ges_base_effect_clip, static void ges_base_effect_clip_class_init (GESBaseEffectClipClass * klass) { + GES_CLIP_CLASS_CAN_ADD_EFFECTS (klass) = TRUE; } static void diff --git a/ges/ges-clip.c b/ges/ges-clip.c index faaf9974d9..fab8f4b595 100644 --- a/ges/ges-clip.c +++ b/ges/ges-clip.c @@ -57,13 +57,15 @@ * * ## Effects * - * For #GESSourceClip-s, you may wish to add additional track elements to - * the clip in the form of #GESEffect-s. These can be added to the source - * clip using ges_container_add(), which will take care of adding the - * effect to the timeline's tracks. The new effect will be placed between - * the clip's core track elements and its other effects. As such, the - * newly added effect will be applied to any source data **before** the - * other existing effects. You can change the ordering of effects using + * Some subclasses (#GESSourceClip and #GESBaseEffect) may also allow + * their objects to have additional non-core #GESBaseEffect-s elements as + * children. These are additional effects that are applied to the output + * data of the core elements. They can be added to the clip using + * ges_container_add(), which will take care of adding the effect to the + * timeline's tracks. The new effect will be placed between the clip's + * core track elements and its other effects. As such, the newly added + * effect will be applied to any source data **before** the other existing + * effects. You can change the ordering of effects using * ges_clip_set_top_effect_index(). */ #ifdef HAVE_CONFIG_H @@ -332,54 +334,76 @@ _compute_height (GESContainer * container) static gboolean _add_child (GESContainer * container, GESTimelineElement * element) { - GList *tmp; + GESClipClass *klass = GES_CLIP_GET_CLASS (GES_CLIP (container)); guint max_prio, min_prio; + GESChildrenControlMode mode = container->children_control_mode; GESClipPrivate *priv = GES_CLIP (container)->priv; g_return_val_if_fail (GES_IS_TRACK_ELEMENT (element), FALSE); /* First make sure we work with a sorted list of GESTimelineElement-s */ _ges_container_sort_children (container); - - /* If the TrackElement is an effect: - * - We add it on top of the list of TrackEffect - * - We put all TrackElements present in the Clip - * which are not BaseEffect on top of them - * FIXME: Let the full control over priorities to the user - */ _get_priority_range (container, &min_prio, &max_prio); - if (GES_IS_BASE_EFFECT (element)) { - GESChildrenControlMode mode = container->children_control_mode; + if (ELEMENT_FLAG_IS_SET (element, GES_TRACK_ELEMENT_IS_CORE)) { + /* NOTE: we are assuming that the core element is **our** core element + * and not from another clip. */ + /* NOTE: Core track elements that are base effects are added like any + * other core clip. In particular, they are *not* added to the list of + * added effects, so we don not increase nb_effects. */ - GST_DEBUG_OBJECT (container, "Adding %ith effect: %" GST_PTR_FORMAT - " Priority %i", priv->nb_effects + 1, element, + /* Always add at the same priority, on top of existing effects */ + _set_priority0 (element, min_prio + priv->nb_effects); + + /* Set the core element to have the same in-point, which we don't + * apply to effects */ + container->children_control_mode = GES_CHILDREN_IGNORE_NOTIFIES; + _set_inpoint0 (element, GES_TIMELINE_ELEMENT_INPOINT (container)); + } else if (GES_CLIP_CLASS_CAN_ADD_EFFECTS (klass) + && GES_IS_BASE_EFFECT (element)) { + GList *tmp; + /* 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. */ + GST_DEBUG_OBJECT (container, "Adding %ith effect: %" GES_FORMAT + " Priority %i", priv->nb_effects + 1, GES_ARGS (element), min_prio + priv->nb_effects); + /* changing priorities, and updating their offset */ tmp = g_list_nth (GES_CONTAINER_CHILDREN (container), priv->nb_effects); - container->children_control_mode = GES_CHILDREN_UPDATE_OFFSETS; - for (; tmp; tmp = tmp->next) { + for (; tmp; tmp = tmp->next) ges_timeline_element_set_priority (GES_TIMELINE_ELEMENT (tmp->data), GES_TIMELINE_ELEMENT_PRIORITY (tmp->data) + 1); - } _set_priority0 (element, min_prio + priv->nb_effects); - container->children_control_mode = mode; priv->nb_effects++; + /* no need to call _ges_container_sort_children (container) since + * there is no change to the ordering yet (this happens after the + * child is actually added) */ + /* The height has already changed (increased by 1) */ + _compute_height (container); } else { - /* We add the track element on top of the effect list */ - _set_priority0 (element, min_prio + priv->nb_effects); + if (GES_IS_BASE_EFFECT (element)) + GST_WARNING_OBJECT (container, "Can not add the effect %" GES_FORMAT + " because it is not a core element created by the clip itself " + "and the %s class does not allow for the adding extra effects", + GES_ARGS (element), G_OBJECT_CLASS_NAME (klass)); + else if (GES_CLIP_CLASS_CAN_ADD_EFFECTS (klass)) + GST_WARNING_OBJECT (container, "Can not add the track element %" + GES_FORMAT " because it is neither a core element created by " + "the clip itself, nor a GESBaseEffect", GES_ARGS (element)); + else + GST_WARNING_OBJECT (container, "Can not add the track element %" + GES_FORMAT " because it is not a core element created by the " + "clip itself", GES_ARGS (element)); + return FALSE; } /* We set the timing value of the child to ours, we avoid infinite loop * making sure the container ignore notifies from the child */ container->children_control_mode = GES_CHILDREN_IGNORE_NOTIFIES; _set_start0 (element, GES_TIMELINE_ELEMENT_START (container)); - /* 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 */ - _set_inpoint0 (element, GES_TIMELINE_ELEMENT_INPOINT (container)); _set_duration0 (element, GES_TIMELINE_ELEMENT_DURATION (container)); - container->children_control_mode = GES_CHILDREN_UPDATE; + container->children_control_mode = mode; return TRUE; } @@ -387,18 +411,26 @@ _add_child (GESContainer * container, GESTimelineElement * element) static gboolean _remove_child (GESContainer * container, GESTimelineElement * element) { - if (GES_IS_BASE_EFFECT (element)) { + if (!ELEMENT_FLAG_IS_SET (element, GES_TRACK_ELEMENT_IS_CORE) + && GES_IS_BASE_EFFECT (element)) { GES_CLIP (container)->priv->nb_effects--; } GST_FIXME_OBJECT (container, "We should set other children prios"); + if (ELEMENT_FLAG_IS_SET (element, GES_TRACK_ELEMENT_IS_CORE)) { + /* When removed, the child is no longer considered a core element. + * This prevents the user from being able to add the removed track + * element to an arbitrary clip */ + ELEMENT_UNSET_FLAG (element, GES_TRACK_ELEMENT_IS_CORE); + } return TRUE; } 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); @@ -412,7 +444,8 @@ _child_removed (GESContainer * container, GESTimelineElement * element) g_signal_handlers_disconnect_by_func (element, _child_priority_changed_cb, container); - if (GES_IS_BASE_EFFECT (element)) { + if (!ELEMENT_FLAG_IS_SET (element, GES_TRACK_ELEMENT_IS_CORE) + && GES_IS_BASE_EFFECT (element)) { GList *tmp; guint32 priority; GESChildrenControlMode mode = container->children_control_mode; @@ -443,6 +476,23 @@ add_clip_to_list (gpointer key, gpointer clip, GList ** list) *list = g_list_prepend (*list, gst_object_ref (clip)); } +static void +_transfer_child (GESClip * from_clip, GESClip * to_clip, + GESTrackElement * child) +{ + gboolean is_core; + /* We need to bump the refcount to avoid the object to be destroyed */ + gst_object_ref (child); + /* IS_CORE flag is removed when an element is removed from its clip */ + is_core = ELEMENT_FLAG_IS_SET (child, GES_TRACK_ELEMENT_IS_CORE); + ges_container_remove (GES_CONTAINER (from_clip), + GES_TIMELINE_ELEMENT (child)); + if (is_core) + ELEMENT_SET_FLAG (child, GES_TRACK_ELEMENT_IS_CORE); + ges_container_add (GES_CONTAINER (to_clip), GES_TIMELINE_ELEMENT (child)); + gst_object_unref (child); +} + static GList * _ungroup (GESContainer * container, gboolean recursive) { @@ -489,14 +539,8 @@ _ungroup (GESContainer * container, gboolean recursive) } /* Move trackelement to the container it is supposed to land into */ - if (tmpclip != clip) { - /* We need to bump the refcount to avoid the object to be destroyed */ - gst_object_ref (track_element); - ges_container_remove (container, GES_TIMELINE_ELEMENT (track_element)); - ges_container_add (GES_CONTAINER (tmpclip), - GES_TIMELINE_ELEMENT (track_element)); - gst_object_unref (track_element); - } + if (tmpclip != clip) + _transfer_child (clip, tmpclip, track_element); } g_list_free_full (children, gst_object_unref); g_hash_table_foreach (_tracktype_clip, (GHFunc) add_clip_to_list, &ret); @@ -636,13 +680,9 @@ _group (GList * containers) GList *children = ges_container_get_children (GES_CONTAINER (cclip), FALSE); for (tmpelement = children; tmpelement; tmpelement = tmpelement->next) { - GESTimelineElement *celement = GES_TIMELINE_ELEMENT (tmpelement->data); - - ges_container_remove (GES_CONTAINER (cclip), celement); - ges_container_add (ret, celement); - - supported_formats = supported_formats | - ges_track_element_get_track_type (GES_TRACK_ELEMENT (celement)); + GESTrackElement *celement = GES_TRACK_ELEMENT (tmpelement->data); + _transfer_child (cclip, GES_CLIP (ret), celement); + supported_formats |= ges_track_element_get_track_type (celement); } g_list_free_full (children, gst_object_unref); @@ -665,14 +705,16 @@ _deep_copy (GESTimelineElement * element, GESTimelineElement * copy) { GList *tmp; GESClip *self = GES_CLIP (element), *ccopy = GES_CLIP (copy); + GESTrackElement *el, *el_copy; if (!self->priv->layer) return; for (tmp = GES_CONTAINER_CHILDREN (element); tmp; tmp = tmp->next) { + el = GES_TRACK_ELEMENT (tmp->data); + el_copy = ges_track_element_copy_core (el, TRUE); ccopy->priv->copied_track_elements = - g_list_append (ccopy->priv->copied_track_elements, - ges_timeline_element_copy (tmp->data, TRUE)); + g_list_append (ccopy->priv->copied_track_elements, el_copy); } ccopy->priv->copied_layer = g_object_ref (self->priv->layer); @@ -704,9 +746,11 @@ _paste (GESTimelineElement * element, GESTimelineElement * ref, GESTrackElement *new_trackelement, *trackelement = GES_TRACK_ELEMENT (tmp->data); + /* NOTE: we do not deep copy the track element, we instead call + * ges_track_element_copy_properties explicitly, which is the + * deep_copy for the GESTrackElementClass. */ new_trackelement = - GES_TRACK_ELEMENT (ges_timeline_element_copy (GES_TIMELINE_ELEMENT - (trackelement), FALSE)); + GES_TRACK_ELEMENT (ges_track_element_copy_core (trackelement, FALSE)); if (new_trackelement == NULL) { GST_WARNING_OBJECT (trackelement, "Could not create a copy"); continue; @@ -930,7 +974,6 @@ ges_clip_create_track_elements (GESClip * clip, GESTrackType type) { GList *result = NULL, *tmp, *children; GESClipClass *klass; - guint max_prio, min_prio; gboolean readding_effects_only = TRUE; g_return_val_if_fail (GES_IS_CLIP (clip), NULL); @@ -950,12 +993,16 @@ ges_clip_create_track_elements (GESClip * clip, GESTrackType type) if (!ges_track_element_get_track (child) && ges_track_element_get_track_type (child) & type) { + gboolean is_core = ELEMENT_FLAG_IS_SET (child, GES_TRACK_ELEMENT_IS_CORE); GST_DEBUG_OBJECT (clip, "Removing for reusage: %" GST_PTR_FORMAT, child); result = g_list_append (result, g_object_ref (child)); ges_container_remove (GES_CONTAINER (clip), tmp->data); - if (!GES_IS_BASE_EFFECT (child)) + if (is_core) { + /* set on child since the flag is removed when removed from the clip */ + ELEMENT_SET_FLAG (child, GES_TRACK_ELEMENT_IS_CORE); readding_effects_only = FALSE; + } } } g_list_free_full (children, gst_object_unref); @@ -991,26 +1038,22 @@ ges_clip_create_track_elements (GESClip * clip, GESTrackType type) * recreated. */ if (readding_effects_only) { - result = g_list_concat (result, klass->create_track_elements (clip, type)); + GList *track_elements = klass->create_track_elements (clip, 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); } - _get_priority_range (GES_CONTAINER (clip), &min_prio, &max_prio); for (tmp = result; tmp; tmp = tmp->next) { - GESTimelineElement *elem = tmp->data; - - _set_start0 (elem, GES_TIMELINE_ELEMENT_START (clip)); - /* FIXME: only set the in-point and max-duration for non-core - * elements */ - _set_inpoint0 (elem, GES_TIMELINE_ELEMENT_INPOINT (clip)); - _set_duration0 (elem, GES_TIMELINE_ELEMENT_DURATION (clip)); - - 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)); - - _set_priority0 (elem, min_prio + clip->priv->nb_effects); - - ges_container_add (GES_CONTAINER (clip), elem); + ges_container_add (GES_CONTAINER (clip), GES_TIMELINE_ELEMENT (tmp->data)); } return result; @@ -1241,21 +1284,40 @@ GList * ges_clip_get_top_effects (GESClip * clip) { GList *tmp, *ret; - guint i; + GESTimelineElement *child; g_return_val_if_fail (GES_IS_CLIP (clip), NULL); GST_DEBUG_OBJECT (clip, "Getting the %i top effects", clip->priv->nb_effects); ret = NULL; - for (tmp = GES_CONTAINER_CHILDREN (clip), i = 0; - i < clip->priv->nb_effects; tmp = tmp->next, i++) { - ret = g_list_append (ret, gst_object_ref (tmp->data)); + for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next) { + child = tmp->data; + if (GES_IS_BASE_EFFECT (child) + && !ELEMENT_FLAG_IS_SET (child, GES_TRACK_ELEMENT_IS_CORE)) + ret = g_list_append (ret, gst_object_ref (child)); } return g_list_sort (ret, (GCompareFunc) element_start_compare); } +static gboolean +_is_added_effect (GESClip * clip, GESBaseEffect * effect) +{ + if (GES_TIMELINE_ELEMENT_PARENT (effect) != GES_TIMELINE_ELEMENT (clip)) { + GST_WARNING_OBJECT (clip, "The effect %" GES_FORMAT + " doe not belong to this clip", GES_ARGS (effect)); + return FALSE; + } + if (ELEMENT_FLAG_IS_SET (effect, GES_TRACK_ELEMENT_IS_CORE)) { + GST_WARNING_OBJECT (clip, "The effect %" GES_FORMAT " is not a top " + "effect of this clip (it is a core element of the clip)", + GES_ARGS (effect)); + return FALSE; + } + return TRUE; +} + /** * ges_clip_get_top_effect_index: * @clip: A #GESClip @@ -1277,6 +1339,8 @@ ges_clip_get_top_effect_index (GESClip * clip, GESBaseEffect * effect) g_return_val_if_fail (GES_IS_CLIP (clip), -1); g_return_val_if_fail (GES_IS_BASE_EFFECT (effect), -1); + if (!_is_added_effect (clip, effect)) + return -1; _get_priority_range (GES_CONTAINER (clip), &min_prio, &max_prio); @@ -1322,12 +1386,12 @@ ges_clip_set_top_effect_index (GESClip * clip, GESBaseEffect * effect, GESTrackElement *track_element; g_return_val_if_fail (GES_IS_CLIP (clip), FALSE); + g_return_val_if_fail (GES_IS_BASE_EFFECT (effect), FALSE); - track_element = GES_TRACK_ELEMENT (effect); - if (G_UNLIKELY (GES_CLIP (GES_TIMELINE_ELEMENT_PARENT (track_element)) != - clip)) + if (!_is_added_effect (clip, effect)) return FALSE; + track_element = GES_TRACK_ELEMENT (effect); current_prio = _PRIORITY (track_element); _get_priority_range (GES_CONTAINER (clip), &min_prio, &max_prio); @@ -1472,29 +1536,6 @@ ges_clip_split (GESClip * clip, guint64 position) inpoint + old_duration * media_duration_factor); _set_duration0 (GES_TIMELINE_ELEMENT (new_object), new_duration); - /* FIXME: this does not check that the new duration of the clip does - * not break the allowed configurations of a timeline! - * E.g., consider a layer with two clips: - * - * [ clip0 ] - * [ clip1 ] - * 0 1 2 3 4 5 6 7 - timeline time - * - * If we split clip1 at time 4 then we would have - * - * [ clip0 ] - * [ clip1 ] - * [ new-clip] - * 0 1 2 3 4 5 6 7 - timeline time - * - * This means that clip1 is entirely covered by clip0! This is allowed - * to occur using ges_clip_split()! - * - * Note this early setting of the duration does not perform such a - * check. Moreover, the later setting of the duration at the end of this - * function also does not perform this check because we set the - * GES_TIMELINE_ELEMENT_SET_SIMPLE flag! - */ _DURATION (clip) = old_duration; g_object_notify (G_OBJECT (clip), "duration"); @@ -1507,25 +1548,12 @@ ges_clip_split (GESClip * clip, guint64 position) GESTrackElement *new_trackelement, *trackelement = GES_TRACK_ELEMENT (tmp->data); - new_trackelement = - GES_TRACK_ELEMENT (ges_timeline_element_copy (GES_TIMELINE_ELEMENT - (trackelement), FALSE)); + new_trackelement = ges_track_element_copy_core (trackelement, FALSE); if (new_trackelement == NULL) { GST_WARNING_OBJECT (trackelement, "Could not create a copy"); continue; } - /* Set 'new' track element timing propeties */ - _set_start0 (GES_TIMELINE_ELEMENT (new_trackelement), position); - /* FIXME: not all track elements should have their inpoint set, - * e.g. transitions or most effects. But how can we determine - * whether a transition should *always* have a 0 inpoint, or - * whether a transition actually uses the inpoint, but it just so - * happens to be set to 0? */ - _set_inpoint0 (GES_TIMELINE_ELEMENT (new_trackelement), - inpoint + old_duration * media_duration_factor); - _set_duration0 (GES_TIMELINE_ELEMENT (new_trackelement), new_duration); - 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-clip.h b/ges/ges-clip.h index 700ece943d..8051fb5e29 100644 --- a/ges/ges-clip.h +++ b/ges/ges-clip.h @@ -39,6 +39,15 @@ G_BEGIN_DECLS typedef struct _GESClipPrivate GESClipPrivate; +/** + * GES_CLIP_CLASS_CAN_ADD_EFFECTS: + * @klass: A #GESClipClass + * + * Whether the class allows for the user to add additional non-core + * #GESBaseEffect-s to clips from this class. + */ +#define GES_CLIP_CLASS_CAN_ADD_EFFECTS(klass) ((GES_CLIP_CLASS (klass))->ABI.abi.can_add_effects) + /** * GESFillTrackElementFunc: * @clip: The #GESClip controlling the track elements @@ -114,6 +123,9 @@ struct _GESClip * @create_track_elements: Method to create the (multiple) core * #GESTrackElement-s of a clip of this class. If create_track_element() * is implemented, this should be kept as the default implementation + * @can_add_effects: Whether the user can add additional non-core + * #GESBaseEffect-s to clips from this class, to be applied to the output + * data of the core elements. */ struct _GESClipClass { @@ -126,7 +138,12 @@ struct _GESClipClass /*< private >*/ /* Padding for API extension */ - gpointer _ges_reserved[GES_PADDING_LARGE]; + union { + gpointer _ges_reserved[GES_PADDING_LARGE]; + struct { + gboolean can_add_effects; + } abi; + } ABI; }; /**************************************************** diff --git a/ges/ges-container.c b/ges/ges-container.c index 0c42ce8346..41c548db3a 100644 --- a/ges/ges-container.c +++ b/ges/ges-container.c @@ -755,7 +755,7 @@ ges_container_add (GESContainer * container, GESTimelineElement * child) if (class->add_child) { if (class->add_child (container, child) == FALSE) { container->children_control_mode = GES_CHILDREN_UPDATE; - GST_WARNING_OBJECT (container, "Erreur adding child %p", child); + GST_WARNING_OBJECT (container, "Error adding child %p", child); return FALSE; } } diff --git a/ges/ges-internal.h b/ges/ges-internal.h index c22ba815d3..e3b08ec57c 100644 --- a/ges/ges-internal.h +++ b/ges/ges-internal.h @@ -410,8 +410,10 @@ G_GNUC_INTERNAL void ges_track_element_copy_properties (GESTimelineElem GESTimelineElement * elementcopy); G_GNUC_INTERNAL void ges_track_element_copy_bindings (GESTrackElement *element, - GESTrackElement *new_element, - guint64 position); + GESTrackElement *new_element, + guint64 position); +G_GNUC_INTERNAL GESTrackElement * ges_track_element_copy_core (GESTrackElement * self, + gboolean deep); G_GNUC_INTERNAL GstElement* ges_source_create_topbin(const gchar* bin_name, GstElement* sub_element, GPtrArray* elements); G_GNUC_INTERNAL void ges_track_set_caps(GESTrack* track, @@ -436,6 +438,7 @@ typedef enum { GES_CLIP_IS_MOVING = (1 << 0), GES_TIMELINE_ELEMENT_SET_SIMPLE = (1 << 1), + GES_TRACK_ELEMENT_IS_CORE = (1 << 2), } GESTimelineElementFlags; G_GNUC_INTERNAL gdouble ges_timeline_element_get_media_duration_factor(GESTimelineElement *self); diff --git a/ges/ges-source-clip.c b/ges/ges-source-clip.c index 21c9f9562b..322072f389 100644 --- a/ges/ges-source-clip.c +++ b/ges/ges-source-clip.c @@ -21,7 +21,16 @@ /** * SECTION:gessourceclip * @title: GESSourceClip - * @short_description: Base Class for sources of a GESLayer + * @short_description: Base Class for sources of a #GESLayer + * + * #GESSourceClip-s are clips whose core elements are #GESSource-s. + * + * ## Effects + * + * #GESSourceClip-s can also have #GESBaseEffect-s added as non-core + * elements. These effects are applied to the core sources of the clip + * that they share a #GESTrack with. See #GESClip for how to add and move + * these effects from the clip. */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -80,6 +89,8 @@ ges_source_clip_class_init (GESSourceClipClass * klass) object_class->get_property = ges_source_clip_get_property; object_class->set_property = ges_source_clip_set_property; object_class->finalize = ges_source_clip_finalize; + + GES_CLIP_CLASS_CAN_ADD_EFFECTS (klass) = TRUE; } static void diff --git a/ges/ges-timeline-element.c b/ges/ges-timeline-element.c index b4c89d9f4d..b1a3c37ea1 100644 --- a/ges/ges-timeline-element.c +++ b/ges/ges-timeline-element.c @@ -1543,7 +1543,7 @@ ges_timeline_element_copy (GESTimelineElement * self, gboolean deep) GESTimelineElement *ret = NULL; - g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE); + g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), NULL); klass = GES_TIMELINE_ELEMENT_GET_CLASS (self); diff --git a/ges/ges-timeline.c b/ges/ges-timeline.c index f8cb45c6d6..6d07b09b09 100644 --- a/ges/ges-timeline.c +++ b/ges/ges-timeline.c @@ -1433,9 +1433,7 @@ clip_track_element_added_cb (GESClip * clip, /* FIXME: in both cases track_element is removed from the clip! */ g_clear_object (&existing_src); - track_element_copy = - GES_TRACK_ELEMENT (ges_timeline_element_copy (GES_TIMELINE_ELEMENT - (track_element), TRUE)); + track_element_copy = ges_track_element_copy_core (track_element, TRUE); GST_LOG_OBJECT (timeline, "Trying to add %p to track %p", track_element_copy, track); diff --git a/ges/ges-track-element.c b/ges/ges-track-element.c index dd62c5bb6a..3d1d2ae640 100644 --- a/ges/ges-track-element.c +++ b/ges/ges-track-element.c @@ -1250,6 +1250,19 @@ ges_track_element_copy_properties (GESTimelineElement * element, g_free (specs); } +/* copy an element, as well as its GES_TRACK_ELEMENT_IS_CORE flag */ +GESTrackElement * +ges_track_element_copy_core (GESTrackElement * self, gboolean deep) +{ + GESTimelineElement *copy = + ges_timeline_element_copy (GES_TIMELINE_ELEMENT (self), deep); + if (!copy) + return NULL; + if (ELEMENT_FLAG_IS_SET (self, GES_TRACK_ELEMENT_IS_CORE)) + ELEMENT_SET_FLAG (copy, GES_TRACK_ELEMENT_IS_CORE); + return GES_TRACK_ELEMENT (copy); +} + static void _split_binding (GESTrackElement * element, GESTrackElement * new_element, guint64 position, GstTimedValueControlSource * source, diff --git a/tests/check/ges/clip.c b/tests/check/ges/clip.c index 238671ddb2..d84ba63a20 100644 --- a/tests/check/ges/clip.c +++ b/tests/check/ges/clip.c @@ -22,15 +22,6 @@ #include #include -void count_cb (GObject * obj, GParamSpec * pspec, gint * count); -void test_children_time_val (GESClip * clip, const gchar * prop, - GstClockTime val); -void test_children_time_setter (GESClip * clip, GESTimelineElement * child, - const gchar * prop, gboolean (*setter) (GESTimelineElement *, GstClockTime), - GstClockTime val1, GstClockTime val2); -void test_children_time_setting_on_clip (GESClip * clip, - GESTimelineElement * child); - GST_START_TEST (test_object_properties) { GESClip *clip; @@ -738,56 +729,70 @@ GST_START_TEST (test_effects_priorities) GST_END_TEST; -void -count_cb (GObject * obj, GParamSpec * pspec, gint * count) +static void +_count_cb (GObject * obj, GParamSpec * pspec, gint * count) { *count = *count + 1; } -void -test_children_time_val (GESClip * clip, const gchar * prop, GstClockTime val) -{ - GList *tmp; - GstClockTime read_val; - - g_object_get (clip, prop, &read_val, NULL); - assert_equals_uint64 (read_val, val); - for (tmp = GES_CONTAINER_CHILDREN (clip); tmp != NULL; tmp = tmp->next) { - g_object_get (tmp->data, prop, &read_val, NULL); - assert_equals_uint64 (read_val, val); - } +#define _assert_children_time_setter(clip, child, prop, setter, val1, val2) \ +{ \ + gint clip_count = 0; \ + gint child_count = 0; \ + gchar *notify_name = g_strconcat ("notify::", prop, NULL); \ + gchar *clip_name = GES_TIMELINE_ELEMENT_NAME (clip); \ + gchar *child_name = NULL; \ + g_signal_connect (clip, notify_name, G_CALLBACK (_count_cb), \ + &clip_count); \ + if (child) { \ + child_name = GES_TIMELINE_ELEMENT_NAME (child); \ + g_signal_connect (child, notify_name, G_CALLBACK (_count_cb), \ + &child_count); \ + } \ + \ + fail_unless (setter (GES_TIMELINE_ELEMENT (clip), val1), \ + "Failed to set the %s property for clip %s", prop, clip_name); \ + assert_clip_children_time_val (clip, prop, val1); \ + \ + fail_unless (clip_count == 1, "The callback for the %s property was " \ + "called %i times for clip %s, rather than once", \ + prop, clip_count, clip_name); \ + if (child) { \ + fail_unless (child_count == 1, "The callback for the %s property " \ + "was called %i times for the child %s of clip %s, rather than " \ + "once", prop, child_count, child_name, clip_name); \ + } \ + \ + clip_count = 0; \ + if (child) { \ + child_count = 0; \ + fail_unless (setter (GES_TIMELINE_ELEMENT (child), val2), \ + "Failed to set the %s property for the child %s of clip %s", \ + prop, child_name, clip_name); \ + fail_unless (child_count == 1, "The callback for the %s property " \ + "was called %i more times for the child %s of clip %s, rather " \ + "than once more", prop, child_count, child_name, clip_name); \ + } else { \ + fail_unless (setter (GES_TIMELINE_ELEMENT (clip), val2), \ + "Failed to set the %s property for clip %s", prop, clip_name); \ + } \ + assert_clip_children_time_val (clip, prop, val2); \ + \ + fail_unless (clip_count == 1, "The callback for the %s property " \ + "was called %i more times for clip %s, rather than once more", \ + prop, clip_count, clip_name); \ + assert_equals_int (g_signal_handlers_disconnect_by_func (clip, \ + G_CALLBACK (_count_cb), &clip_count), 1); \ + if (child) { \ + assert_equals_int (g_signal_handlers_disconnect_by_func (child, \ + G_CALLBACK (_count_cb), &child_count), 1); \ + } \ + \ + g_free (notify_name); \ } -void -test_children_time_setter (GESClip * clip, GESTimelineElement * child, - const gchar * prop, gboolean (*setter) (GESTimelineElement *, GstClockTime), - GstClockTime val1, GstClockTime val2) -{ - gint clip_count = 0; - gint child_count = 0; - gchar *notify_name = g_strconcat ("notify::", prop, NULL); - - g_signal_connect (clip, notify_name, G_CALLBACK (count_cb), &clip_count); - g_signal_connect (child, notify_name, G_CALLBACK (count_cb), &child_count); - fail_unless (setter (GES_TIMELINE_ELEMENT (clip), val1)); - test_children_time_val (clip, prop, val1); - assert_equals_int (clip_count, 1); - assert_equals_int (child_count, 1); - clip_count = 0; - child_count = 0; - fail_unless (setter (child, val2)); - test_children_time_val (clip, prop, val2); - assert_equals_int (clip_count, 1); - assert_equals_int (child_count, 1); - assert_equals_int (g_signal_handlers_disconnect_by_func (clip, - G_CALLBACK (count_cb), &clip_count), 1); - assert_equals_int (g_signal_handlers_disconnect_by_func (child, - G_CALLBACK (count_cb), &child_count), 1); - g_free (notify_name); -} - -void -test_children_time_setting_on_clip (GESClip * clip, GESTimelineElement * child) +static void +_test_children_time_setting_on_clip (GESClip * clip, GESTimelineElement * child) { /* FIXME: Don't necessarily want to change the inpoint of all the * children if the clip inpoint changes. Really, we would only expect @@ -800,50 +805,66 @@ test_children_time_setting_on_clip (GESClip * clip, GESTimelineElement * child) * test should be changed to only check that source elements have * their inpoint changed, and operation elements have their inpoint * unchanged */ - test_children_time_setter (clip, child, "in-point", - ges_timeline_element_set_inpoint, 23, 52); - test_children_time_setter (clip, child, "start", - ges_timeline_element_set_start, 43, 72); - test_children_time_setter (clip, child, "duration", - ges_timeline_element_set_duration, 53, 12); + _assert_children_time_setter (clip, child, "in-point", + ges_timeline_element_set_inpoint, 11, 101); + _assert_children_time_setter (clip, child, "in-point", + ges_timeline_element_set_inpoint, 51, 1); + _assert_children_time_setter (clip, child, "start", + ges_timeline_element_set_start, 12, 102); + _assert_children_time_setter (clip, child, "start", + ges_timeline_element_set_start, 52, 2); + _assert_children_time_setter (clip, child, "duration", + ges_timeline_element_set_duration, 13, 103); + _assert_children_time_setter (clip, child, "duration", + ges_timeline_element_set_duration, 53, 3); } GST_START_TEST (test_children_time_setters) { GESTimeline *timeline; - GESTrack *audio_track, *video_track; GESLayer *layer; - - GESTimelineElement *effect; - GESClip *clips[] = { - GES_CLIP (ges_transition_clip_new (GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE)), /* operation clip */ - GES_CLIP (ges_test_clip_new ()), /* source clip */ - }; + GESClip *clips[] = { NULL, NULL }; gint i; ges_init (); - audio_track = GES_TRACK (ges_audio_track_new ()); - video_track = GES_TRACK (ges_video_track_new ()); - - timeline = ges_timeline_new (); - fail_unless (ges_timeline_add_track (timeline, audio_track)); - fail_unless (ges_timeline_add_track (timeline, video_track)); + timeline = ges_timeline_new_audio_video (); + fail_unless (timeline); layer = ges_timeline_append_layer (timeline); - effect = GES_TIMELINE_ELEMENT (ges_effect_new ("agingtv")); + clips[0] = + GES_CLIP (ges_transition_clip_new + (GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE)); + clips[1] = GES_CLIP (ges_test_clip_new ()); for (i = 0; i < G_N_ELEMENTS (clips); i++) { GESClip *clip = clips[i]; - fail_unless (ges_container_add (GES_CONTAINER (clip), effect)); - test_children_time_setting_on_clip (clip, effect); + GESContainer *group = GES_CONTAINER (ges_group_new ()); + GList *children; + GESTimelineElement *child; + /* no children */ + _test_children_time_setting_on_clip (clip, NULL); + /* child in timeline */ fail_unless (ges_layer_add_clip (layer, clip)); - test_children_time_setting_on_clip (clip, effect); - fail_unless (ges_container_remove (GES_CONTAINER (clip), effect)); + children = GES_CONTAINER_CHILDREN (clip); + fail_unless (children); + child = GES_TIMELINE_ELEMENT (children->data); + _test_children_time_setting_on_clip (clip, child); + /* clip in a group */ + ges_container_add (group, GES_TIMELINE_ELEMENT (clip)); + _test_children_time_setting_on_clip (clip, child); + /* group is removed from the timeline and destroyed when empty */ + ges_container_remove (group, GES_TIMELINE_ELEMENT (clip)); + /* child not in timeline */ + gst_object_ref (clip); fail_unless (ges_layer_remove_clip (layer, clip)); + children = GES_CONTAINER_CHILDREN (clip); + fail_unless (children); + child = GES_TIMELINE_ELEMENT (children->data); + _test_children_time_setting_on_clip (clip, child); + gst_object_unref (clip); } - gst_object_unref (effect); gst_object_unref (timeline); ges_deinit (); @@ -851,6 +872,59 @@ GST_START_TEST (test_children_time_setters) GST_END_TEST; +GST_START_TEST (test_can_add_effect) +{ + struct CanAddEffectData + { + GESClip *clip; + gboolean can_add_effect; + } clips[6]; + guint i; + gchar *uri; + + ges_init (); + + uri = ges_test_get_audio_video_uri (); + + clips[0] = (struct CanAddEffectData) { + GES_CLIP (ges_test_clip_new ()), TRUE}; + clips[1] = (struct CanAddEffectData) { + GES_CLIP (ges_uri_clip_new (uri)), TRUE}; + clips[2] = (struct CanAddEffectData) { + GES_CLIP (ges_title_clip_new ()), TRUE}; + clips[3] = (struct CanAddEffectData) { + GES_CLIP (ges_effect_clip_new ("agingtv", "audioecho")), TRUE}; + clips[4] = (struct CanAddEffectData) { + GES_CLIP (ges_transition_clip_new + (GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE)), FALSE}; + clips[5] = (struct CanAddEffectData) { + GES_CLIP (ges_text_overlay_clip_new ()), FALSE}; + + g_free (uri); + + for (i = 0; i < G_N_ELEMENTS (clips); i++) { + GESClip *clip = clips[i].clip; + GESTimelineElement *effect = + GES_TIMELINE_ELEMENT (ges_effect_new ("agingtv")); + gst_object_ref_sink (effect); + gst_object_ref_sink (clip); + fail_unless (clip); + if (clips[i].can_add_effect) + fail_unless (ges_container_add (GES_CONTAINER (clip), effect), + "Could not add an effect to clip %s", + GES_TIMELINE_ELEMENT_NAME (clip)); + else + fail_if (ges_container_add (GES_CONTAINER (clip), effect), + "Could add an effect to clip %s, but we expect this to fail", + GES_TIMELINE_ELEMENT_NAME (clip)); + gst_object_unref (effect); + gst_object_unref (clip); + } + + ges_deinit (); +} + +GST_END_TEST; static Suite * ges_suite (void) @@ -869,6 +943,7 @@ ges_suite (void) tcase_add_test (tc_chain, test_clip_find_track_element); 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); return s; } diff --git a/tests/check/ges/effects.c b/tests/check/ges/effects.c index d7cb857232..602c1f2923 100644 --- a/tests/check/ges/effects.c +++ b/tests/check/ges/effects.c @@ -182,15 +182,11 @@ GST_START_TEST (test_effect_clip) GESLayer *layer; GESTrack *track_audio, *track_video; GESEffectClip *effect_clip; - GESEffect *effect, *effect1; - GList *effects, *tmp; - gint i, clip_height; - gint effect_prio = -1; - /* FIXME the order of track type is not well defined */ - guint track_type[4] = { GES_TRACK_TYPE_AUDIO, - GES_TRACK_TYPE_VIDEO, GES_TRACK_TYPE_VIDEO, - GES_TRACK_TYPE_AUDIO - }; + GESEffect *effect, *effect1, *core_effect, *core_effect1; + GList *children, *top_effects, *tmp; + gint clip_height; + gint core_effect_prio; + gint effect_index, effect1_index; ges_init (); @@ -204,45 +200,91 @@ GST_START_TEST (test_effect_clip) ges_timeline_add_layer (timeline, layer); GST_DEBUG ("Create effect"); - effect_clip = ges_effect_clip_new ("agingtv", "audiopanorama"); + /* these are the core video and audio effects for the clip */ + effect_clip = ges_effect_clip_new ("videobalance", "audioecho"); g_object_set (effect_clip, "duration", 25 * GST_SECOND, NULL); ges_layer_add_clip (layer, (GESClip *) effect_clip); + /* core elements should now be created */ + fail_unless (children = GES_CONTAINER_CHILDREN (effect_clip)); + core_effect = GES_EFFECT (children->data); + fail_unless (children = children->next); + core_effect1 = GES_EFFECT (children->data); + fail_unless (children->next == NULL); + + /* both effects are placed at the same priority since they are core + * children of the clip, destined for different tracks */ + core_effect_prio = _PRIORITY (core_effect); + assert_equals_int (core_effect_prio, _PRIORITY (core_effect1)); + g_object_get (effect_clip, "height", &clip_height, NULL); + assert_equals_int (clip_height, 1); + + /* add additional non-core effects */ effect = ges_effect_new ("agingtv"); fail_unless (ges_container_add (GES_CONTAINER (effect_clip), GES_TIMELINE_ELEMENT (effect))); fail_unless (ges_track_element_get_track (GES_TRACK_ELEMENT (effect)) == track_video); + /* placed at a higher priority than the effects */ + core_effect_prio = _PRIORITY (core_effect); + assert_equals_int (core_effect_prio, _PRIORITY (core_effect1)); + fail_unless (_PRIORITY (effect) < core_effect_prio); g_object_get (effect_clip, "height", &clip_height, NULL); - assert_equals_int (clip_height, 3); + assert_equals_int (clip_height, 2); + effect_index = + ges_clip_get_top_effect_index (GES_CLIP (effect_clip), + GES_BASE_EFFECT (effect)); + assert_equals_int (effect_index, 0); + + /* 'effect1' is placed in between the core children and 'effect' */ effect1 = ges_effect_new ("audiopanorama"); fail_unless (ges_container_add (GES_CONTAINER (effect_clip), GES_TIMELINE_ELEMENT (effect1))); fail_unless (ges_track_element_get_track (GES_TRACK_ELEMENT (effect1)) == track_audio); + /* 'effect' is still the highest priority effect, and the core + * elements are at the lowest priority */ + core_effect_prio = _PRIORITY (core_effect); + assert_equals_int (core_effect_prio, _PRIORITY (core_effect1)); + fail_unless (_PRIORITY (effect1) < core_effect_prio); + fail_unless (_PRIORITY (effect1) > _PRIORITY (effect)); g_object_get (effect_clip, "height", &clip_height, NULL); - assert_equals_int (clip_height, 4); + assert_equals_int (clip_height, 3); - effects = ges_clip_get_top_effects (GES_CLIP (effect_clip)); - for (tmp = effects, i = 0; tmp; tmp = tmp->next, i++) { - gint priority = ges_clip_get_top_effect_position (GES_CLIP (effect_clip), - GES_BASE_EFFECT (tmp->data)); - fail_unless (priority > effect_prio); - fail_unless (GES_IS_EFFECT (tmp->data)); - fail_unless (ges_track_element_get_track (GES_TRACK_ELEMENT (tmp->data))-> - type == track_type[i]); - effect_prio = priority; + effect_index = + ges_clip_get_top_effect_index (GES_CLIP (effect_clip), + GES_BASE_EFFECT (effect)); + effect1_index = + ges_clip_get_top_effect_index (GES_CLIP (effect_clip), + GES_BASE_EFFECT (effect1)); + assert_equals_int (effect_index, 0); + assert_equals_int (effect1_index, 1); - gst_object_unref (tmp->data); - } - g_list_free (effects); + /* all effects are children of the effect_clip, ordered by priority */ + fail_unless (children = GES_CONTAINER_CHILDREN (effect_clip)); + fail_unless (children->data == effect); + fail_unless (children = children->next); + fail_unless (children->data == effect1); + fail_unless (children = children->next); + fail_unless (children->data == core_effect); + fail_unless (children = children->next); + fail_unless (children->data == core_effect1); + fail_unless (children->next == NULL); - ges_layer_remove_clip (layer, (GESClip *) effect_clip); + /* but only the additional effects are part of the top effects */ + top_effects = ges_clip_get_top_effects (GES_CLIP (effect_clip)); + fail_unless (tmp = top_effects); + fail_unless (tmp->data == effect); + fail_unless (tmp = tmp->next); + fail_unless (tmp->data == effect1); + fail_unless (tmp->next == NULL); + + g_list_free_full (top_effects, gst_object_unref); gst_object_unref (timeline); @@ -253,14 +295,14 @@ GST_END_TEST; GST_START_TEST (test_priorities_clip) { - GList *effects, *tmp; + GList *top_effects, *tmp; GESTimeline *timeline; GESLayer *layer; - GESEffectClip *effect_clip; - GESTrack *track_audio, *track_video; - GESEffect *effect, *effect1, *audio_effect = NULL, *video_effect = NULL; - - gint effect_prio = -1; + GESClip *effect_clip; + GESTrack *track_audio, *track_video, *track; + GESBaseEffect *effects[6], *audio_effect = NULL, *video_effect = NULL; + gint prev_index, i, num_effects = G_N_ELEMENTS (effects); + guint32 base_prio = MIN_NLE_PRIO + TRANSITIONS_HEIGHT; ges_init (); @@ -274,7 +316,7 @@ GST_START_TEST (test_priorities_clip) ges_timeline_add_layer (timeline, layer); GST_DEBUG ("Create effect"); - effect_clip = ges_effect_clip_new ("agingtv", "audiopanorama"); + effect_clip = GES_CLIP (ges_effect_clip_new ("videobalance", "audioecho")); g_object_set (effect_clip, "duration", 25 * GST_SECOND, NULL); @@ -291,73 +333,134 @@ GST_START_TEST (test_priorities_clip) } fail_unless (GES_IS_EFFECT (audio_effect)); fail_unless (GES_IS_EFFECT (video_effect)); - - /* FIXME This is ridiculus, both effects should have the same priority */ - assert_equals_int (_PRIORITY (audio_effect), - MIN_NLE_PRIO + TRANSITIONS_HEIGHT); - assert_equals_int (_PRIORITY (video_effect), - MIN_NLE_PRIO + TRANSITIONS_HEIGHT + 1); - assert_equals_int (GES_CONTAINER_HEIGHT (effect_clip), 2); - - effect = ges_effect_new ("agingtv"); - GST_DEBUG ("Adding effect to the effect clip %" GST_PTR_FORMAT, effect); - fail_unless (ges_container_add (GES_CONTAINER (effect_clip), - GES_TIMELINE_ELEMENT (effect))); - fail_unless (ges_track_element_get_track (GES_TRACK_ELEMENT (effect)) == - track_video); - assert_equals_int (GES_CONTAINER_HEIGHT (effect_clip), 3); - - effect1 = ges_effect_new ("audiopanorama"); - GST_DEBUG ("Adding effect1 to the effect clip %" GST_PTR_FORMAT, effect1); - fail_unless (ges_container_add (GES_CONTAINER (effect_clip), - GES_TIMELINE_ELEMENT (effect1))); - fail_unless (ges_track_element_get_track (GES_TRACK_ELEMENT (effect1)) == + fail_unless (ges_track_element_get_track (GES_TRACK_ELEMENT (audio_effect)) == track_audio); + fail_unless (ges_track_element_get_track (GES_TRACK_ELEMENT (video_effect)) == + track_video); - fail_unless (ges_clip_set_top_effect_priority (GES_CLIP (effect_clip), - GES_BASE_EFFECT (effect1), 0)); + /* both the core effects have the same priority */ + assert_equals_int (_PRIORITY (audio_effect), base_prio); + assert_equals_int (_PRIORITY (video_effect), base_prio); + assert_equals_int (GES_CONTAINER_HEIGHT (effect_clip), 1); + + /* can not change their priority using the top effect methods since + * they are not top effects */ + fail_unless (ges_clip_set_top_effect_index (effect_clip, audio_effect, 1) + == FALSE); + fail_unless (ges_clip_set_top_effect_index (effect_clip, video_effect, 0) + == FALSE); + + /* adding non-core effects */ + GST_DEBUG ("Adding effects to the effect clip "); + for (i = 0; i < num_effects; i++) { + if (i % 2) + effects[i] = GES_BASE_EFFECT (ges_effect_new ("agingtv")); + else + effects[i] = GES_BASE_EFFECT (ges_effect_new ("audiopanorama")); + fail_unless (ges_container_add (GES_CONTAINER (effect_clip), + GES_TIMELINE_ELEMENT (effects[i]))); + assert_equals_int (GES_CONTAINER_HEIGHT (effect_clip), 2 + i); + track = ges_track_element_get_track (GES_TRACK_ELEMENT (effects[i])); + if (i % 2) + fail_unless (track == track_video); + else + fail_unless (track == track_audio); + } + + /* change top effect index */ + for (i = 0; i < num_effects; i++) { + assert_equals_int (ges_clip_get_top_effect_index (effect_clip, effects[i]), + i); + assert_equals_int (_PRIORITY (effects[i]), i + base_prio); + } + + assert_equals_int (_PRIORITY (video_effect), num_effects + base_prio); + assert_equals_int (_PRIORITY (audio_effect), num_effects + base_prio); assert_equals_int (_PRIORITY (effect_clip), 1); + assert_equals_int (GES_CONTAINER_HEIGHT (effect_clip), num_effects + 1); - assert_equals_int (_PRIORITY (effect), 3 + MIN_NLE_PRIO + TRANSITIONS_HEIGHT); - assert_equals_int (_PRIORITY (effect1), - 0 + MIN_NLE_PRIO + TRANSITIONS_HEIGHT); + /* moving 4th effect to index 1 should only change the priority of + * effects 1, 2, 3, and 4 because these lie between the new index (1) + * and the old index (4). */ + fail_unless (ges_clip_set_top_effect_index (effect_clip, effects[4], 1)); - assert_equals_int (GES_CONTAINER_HEIGHT (effect_clip), 4); + assert_equals_int (_PRIORITY (effects[0]), 0 + base_prio); + assert_equals_int (_PRIORITY (effects[1]), 2 + base_prio); + assert_equals_int (_PRIORITY (effects[2]), 3 + base_prio); + assert_equals_int (_PRIORITY (effects[3]), 4 + base_prio); + assert_equals_int (_PRIORITY (effects[4]), 1 + base_prio); + assert_equals_int (_PRIORITY (effects[5]), 5 + base_prio); - fail_unless (ges_clip_set_top_effect_priority (GES_CLIP (effect_clip), - GES_BASE_EFFECT (effect1), 3)); - assert_equals_int (_PRIORITY (effect), 2 + MIN_NLE_PRIO + TRANSITIONS_HEIGHT); - assert_equals_int (_PRIORITY (effect1), - 3 + MIN_NLE_PRIO + TRANSITIONS_HEIGHT); - assert_equals_int (GES_CONTAINER_HEIGHT (effect_clip), 4); + /* everything else stays the same */ + assert_equals_int (_PRIORITY (video_effect), num_effects + base_prio); + assert_equals_int (_PRIORITY (audio_effect), num_effects + base_prio); + assert_equals_int (_PRIORITY (effect_clip), 1); + assert_equals_int (GES_CONTAINER_HEIGHT (effect_clip), num_effects + 1); - effects = ges_clip_get_top_effects (GES_CLIP (effect_clip)); - for (tmp = effects; tmp; tmp = tmp->next) { - gint priority = ges_clip_get_top_effect_position (GES_CLIP (effect_clip), - GES_BASE_EFFECT (tmp->data)); - fail_unless (priority > effect_prio); - fail_unless (GES_IS_EFFECT (tmp->data)); - effect_prio = priority; + /* move back */ + fail_unless (ges_clip_set_top_effect_index (effect_clip, effects[4], 4)); - gst_object_unref (tmp->data); + for (i = 0; i < num_effects; i++) { + assert_equals_int (ges_clip_get_top_effect_index (effect_clip, effects[i]), + i); + assert_equals_int (_PRIORITY (effects[i]), i + base_prio); } - g_list_free (effects); - assert_equals_int (GES_CONTAINER_HEIGHT (effect_clip), 4); - effects = ges_clip_get_top_effects (GES_CLIP (effect_clip)); - effect_prio = 0; - for (tmp = effects; tmp; tmp = tmp->next) { - gint priority = ges_clip_get_top_effect_position (GES_CLIP (effect_clip), + assert_equals_int (_PRIORITY (video_effect), num_effects + base_prio); + assert_equals_int (_PRIORITY (audio_effect), num_effects + base_prio); + assert_equals_int (_PRIORITY (effect_clip), 1); + assert_equals_int (GES_CONTAINER_HEIGHT (effect_clip), num_effects + 1); + + /* moving 2nd effect to index 4 should only change the priority of + * effects 2, 3 and 4 because these lie between the new index (4) and + * the old index (2). */ + + fail_unless (ges_clip_set_top_effect_index (effect_clip, effects[2], 4)); + + assert_equals_int (_PRIORITY (effects[0]), 0 + base_prio); + assert_equals_int (_PRIORITY (effects[1]), 1 + base_prio); + assert_equals_int (_PRIORITY (effects[2]), 4 + base_prio); + assert_equals_int (_PRIORITY (effects[3]), 2 + base_prio); + assert_equals_int (_PRIORITY (effects[4]), 3 + base_prio); + assert_equals_int (_PRIORITY (effects[5]), 5 + base_prio); + + /* everything else stays the same */ + assert_equals_int (_PRIORITY (video_effect), num_effects + base_prio); + assert_equals_int (_PRIORITY (audio_effect), num_effects + base_prio); + assert_equals_int (_PRIORITY (effect_clip), 1); + assert_equals_int (GES_CONTAINER_HEIGHT (effect_clip), num_effects + 1); + + /* move 4th effect to index 0 should only change the priority of + * effects 0, 1, 3 and 4 because these lie between the new index (0) and + * the old index (3) */ + + fail_unless (ges_clip_set_top_effect_index (effect_clip, effects[4], 0)); + + assert_equals_int (_PRIORITY (effects[0]), 1 + base_prio); + assert_equals_int (_PRIORITY (effects[1]), 2 + base_prio); + assert_equals_int (_PRIORITY (effects[2]), 4 + base_prio); + assert_equals_int (_PRIORITY (effects[3]), 3 + base_prio); + assert_equals_int (_PRIORITY (effects[4]), 0 + base_prio); + assert_equals_int (_PRIORITY (effects[5]), 5 + base_prio); + + /* everything else stays the same */ + assert_equals_int (_PRIORITY (video_effect), num_effects + base_prio); + assert_equals_int (_PRIORITY (audio_effect), num_effects + base_prio); + assert_equals_int (_PRIORITY (effect_clip), 1); + assert_equals_int (GES_CONTAINER_HEIGHT (effect_clip), num_effects + 1); + + /* make sure top effects are ordered by index */ + top_effects = ges_clip_get_top_effects (effect_clip); + prev_index = -1; + for (tmp = top_effects; tmp; tmp = tmp->next) { + gint index = ges_clip_get_top_effect_index (effect_clip, GES_BASE_EFFECT (tmp->data)); - fail_unless (priority >= effect_prio, "%d >= %d", priority, effect_prio); + fail_unless (index >= 0); + fail_unless (index > prev_index); fail_unless (GES_IS_EFFECT (tmp->data)); - effect_prio = priority; - - gst_object_unref (tmp->data); + prev_index = index; } - g_list_free (effects); - - ges_layer_remove_clip (layer, (GESClip *) effect_clip); + g_list_free_full (top_effects, gst_object_unref); gst_object_unref (timeline); @@ -526,7 +629,7 @@ GST_START_TEST (test_split_clip_effect_priorities) ges_init (); timeline = ges_timeline_new (); - layer = ges_layer_new (); + layer = ges_timeline_append_layer (timeline); track_video = GES_TRACK (ges_video_track_new ()); g_object_set (timeline, "auto-transition", TRUE, NULL); @@ -547,6 +650,7 @@ GST_START_TEST (test_split_clip_effect_priorities) assert_equals_uint64 (GES_TIMELINE_ELEMENT_PRIORITY (source), 4); nclip = ges_clip_split (clip, GST_SECOND); + fail_unless (nclip); neffect = ges_clip_find_track_element (nclip, NULL, GES_TYPE_EFFECT); nsource = ges_clip_find_track_element (nclip, NULL, GES_TYPE_VIDEO_SOURCE); diff --git a/tests/check/ges/test-utils.h b/tests/check/ges/test-utils.h index 971079ec77..b225c766b9 100644 --- a/tests/check/ges/test-utils.h +++ b/tests/check/ges/test-utils.h @@ -94,6 +94,29 @@ 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));\ } +/* 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) \ +{ \ + GList *tmp; \ + GstClockTime read_val; \ + gchar *name = GES_TIMELINE_ELEMENT (clip)->name; \ + 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 %" \ + GST_TIME_FORMAT, property, name, GST_TIME_ARGS (read_val), \ + GST_TIME_ARGS (cmp)); \ + for (tmp = GES_CONTAINER_CHILDREN (clip); tmp != NULL; \ + 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)); \ + } \ +} + #define check_layer(clip, layer_prio) { \ fail_unless (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (clip) == (layer_prio), \ "%s in layer %d instead of %d", GES_TIMELINE_ELEMENT_NAME (clip), \