From 0e11ec413748ec4a721a5a5185f558f62e5d36a7 Mon Sep 17 00:00:00 2001 From: Mathieu Duponchelle Date: Sun, 24 Jul 2011 02:49:36 +0200 Subject: [PATCH] ges: add an auto-transition to the layer API: GESTimelineLayer.auto_transition property --- ges/ges-timeline-layer.c | 307 ++++++++++++++++++++++++++++++++++++++- ges/ges-timeline-layer.h | 8 +- tests/check/ges/layer.c | 41 ++++++ 3 files changed, 348 insertions(+), 8 deletions(-) diff --git a/ges/ges-timeline-layer.c b/ges/ges-timeline-layer.c index 037d208e59..9b1213e857 100644 --- a/ges/ges-timeline-layer.c +++ b/ges/ges-timeline-layer.c @@ -32,25 +32,41 @@ #include "gesmarshal.h" #include "ges-timeline-layer.h" #include "ges.h" +#include "ges-timeline-source.h" #define LAYER_HEIGHT 10 +void track_object_added_cb (GESTimelineObject * object, + GESTrackObject * track_object, GESTimelineLayer * layer); +static void track_object_start_changed_cb (GESTrackObject * track_object, + GParamSpec * arg G_GNUC_UNUSED, GESTimelineObject * object); +void calculate_transition (GESTrackObject * track_object, + GESTimelineObject * object); + +void +timeline_object_height_changed_cb (GESTimelineObject * obj, + GESTrackEffect * tr_eff, GESTimelineObject * second_obj); + G_DEFINE_TYPE (GESTimelineLayer, ges_timeline_layer, G_TYPE_INITIALLY_UNOWNED); struct _GESTimelineLayerPrivate { /*< private > */ - GSList *objects_start; /* The TimelineObjects sorted by start and + GList *objects_start; /* The TimelineObjects sorted by start and * priority */ guint32 priority; /* The priority of the layer within the * containing timeline */ + + gboolean auto_transition; }; enum { PROP_0, PROP_PRIORITY, + PROP_AUTO_TRANSITION, + PROP_LAST }; enum @@ -64,6 +80,11 @@ static guint ges_timeline_layer_signals[LAST_SIGNAL] = { 0 }; static gboolean ges_timeline_layer_resync_priorities (GESTimelineLayer * layer); +static GList *track_get_by_layer (GESTrackObject * track_object); + +static void compare (GList * compared, GESTrackObject * track_object, + gboolean ahead); + static void ges_timeline_layer_get_property (GObject * object, guint property_id, GValue * value, GParamSpec * pspec) @@ -74,6 +95,9 @@ ges_timeline_layer_get_property (GObject * object, guint property_id, case PROP_PRIORITY: g_value_set_uint (value, layer->priv->priority); break; + case PROP_AUTO_TRANSITION: + g_value_set_boolean (value, layer->priv->auto_transition); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } @@ -89,6 +113,10 @@ ges_timeline_layer_set_property (GObject * object, guint property_id, case PROP_PRIORITY: ges_timeline_layer_set_priority (layer, g_value_get_uint (value)); break; + case PROP_AUTO_TRANSITION: + ges_timeline_layer_set_auto_transition (layer, + g_value_get_boolean (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } @@ -132,6 +160,15 @@ ges_timeline_layer_class_init (GESTimelineLayerClass * klass) g_param_spec_uint ("priority", "Priority", "The priority of the layer", 0, G_MAXUINT, 0, G_PARAM_READWRITE)); + /** + * GESTimelineLayer:auto_transitioning + * + * Sets whether transitions are added automatically when timeline objects overlap + */ + g_object_class_install_property (object_class, PROP_AUTO_TRANSITION, + g_param_spec_boolean ("auto-transition", "Auto-Transition", + "whether the transitions are added", FALSE, G_PARAM_READWRITE)); + /** * GESTimelineLayer::object-added * @layer: the #GESTimelineLayer @@ -157,7 +194,6 @@ ges_timeline_layer_class_init (GESTimelineLayerClass * klass) G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineLayerClass, object_removed), NULL, NULL, ges_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GES_TYPE_TIMELINE_OBJECT); - } static void @@ -167,6 +203,7 @@ ges_timeline_layer_init (GESTimelineLayer * self) GES_TYPE_TIMELINE_LAYER, GESTimelineLayerPrivate); self->priv->priority = 0; + self->priv->auto_transition = FALSE; self->min_gnl_priority = 0; self->max_gnl_priority = 9; } @@ -224,6 +261,37 @@ objects_start_compare (GESTimelineObject * a, GESTimelineObject * b) * if the @layer refuses to add the object. */ +static GList * +track_get_by_layer (GESTrackObject * track_object) +{ + GESTrack *track; + GList *tck_objects_list = NULL, *tmp = NULL, *return_list = NULL; + GESTimelineLayer *layer; + GESTimelineObject *tl_obj; + gint priority, compared_priority; + + track = ges_track_object_get_track (track_object); + tl_obj = ges_track_object_get_timeline_object (track_object); + layer = ges_timeline_object_get_layer (tl_obj); + priority = ges_timeline_layer_get_priority (layer); + + tck_objects_list = ges_track_get_objects (track); + for (tmp = tck_objects_list; tmp; tmp = tmp->next) { + tl_obj = ges_track_object_get_timeline_object (tmp->data); + layer = ges_timeline_object_get_layer (tl_obj); + compared_priority = ges_timeline_layer_get_priority (layer); + if (compared_priority == priority) { + g_object_ref (tmp->data); + return_list = g_list_append (return_list, tmp->data); + } + } + + for (tmp = tck_objects_list; tmp; tmp = tmp->next) + g_object_unref (tmp->data); + g_list_free (tck_objects_list); + return return_list; +} + gboolean ges_timeline_layer_add_object (GESTimelineLayer * layer, GESTimelineObject * object) @@ -245,9 +313,17 @@ ges_timeline_layer_add_object (GESTimelineLayer * layer, /* Take a reference to the object and store it stored by start/priority */ layer->priv->objects_start = - g_slist_insert_sorted (layer->priv->objects_start, object, + g_list_insert_sorted (layer->priv->objects_start, object, (GCompareFunc) objects_start_compare); + if (layer->priv->auto_transition) { + if (GES_IS_TIMELINE_SOURCE (object) + && (ges_timeline_layer_get_priority (layer) != 99)) { + g_signal_connect (G_OBJECT (object), "track-object-added", + G_CALLBACK (track_object_added_cb), layer); + } + } + /* Inform the object it's now in this layer */ ges_timeline_object_set_layer (object, layer); @@ -261,7 +337,7 @@ ges_timeline_layer_add_object (GESTimelineLayer * layer, prio = GES_TIMELINE_OBJECT_PRIORITY (object); if (minprio + prio > (maxprio)) { GST_WARNING ("%p is out of the layer %p space, setting its priority to " - "setting its priority %d to the maximum priority of the layer %d", + "setting its priority %d to failthe maximum priority of the layer %d", object, layer, prio, maxprio - minprio); ges_timeline_object_set_priority (object, LAYER_HEIGHT - 1); } @@ -276,6 +352,190 @@ ges_timeline_layer_add_object (GESTimelineLayer * layer, return TRUE; } +void +track_object_added_cb (GESTimelineObject * object, + GESTrackObject * track_object, GESTimelineLayer * layer) +{ + if (GES_IS_TRACK_SOURCE (track_object)) { + g_signal_connect (G_OBJECT (track_object), "notify::start", + G_CALLBACK (track_object_start_changed_cb), object); + calculate_transition (track_object, object); + } + + g_object_unref (layer); + return; +} + +void +timeline_object_height_changed_cb (GESTimelineObject * obj, + GESTrackEffect * tr_eff, GESTimelineObject * second_obj) +{ + gint priority, height; + g_object_get (obj, "height", &height, "priority", &priority, NULL); + g_object_set (second_obj, "priority", priority + height, NULL); +} + +static void +track_object_start_changed_cb (GESTrackObject * track_object, + GParamSpec * arg G_GNUC_UNUSED, GESTimelineObject * object) +{ + if (GES_IS_TRACK_SOURCE (track_object)) { + calculate_transition (track_object, object); + } +} + +void +calculate_transition (GESTrackObject * track_object, GESTimelineObject * object) +{ + GList *list, *cur, *compared, *compared_next, *tmp; + + list = track_get_by_layer (track_object); + cur = g_list_find (list, track_object); + + compared = cur->prev; + + if (compared == NULL) + goto next; + + while (!GES_IS_TRACK_SOURCE (compared->data)) { + compared = compared->prev; + if (compared == NULL) + goto next; + } + + compare (compared, track_object, TRUE); + +next: + cur = g_list_find (list, track_object); + compared_next = cur->next; + + if (compared_next == NULL) + return; + + while (!GES_IS_TRACK_SOURCE (compared_next->data)) { + compared_next = compared_next->next; + if (compared_next == NULL) + return; + } + + compare (compared_next, track_object, FALSE); + + for (tmp = list; tmp; tmp = tmp->next) + g_object_unref (tmp->data); + g_list_free (list); +} + + +static void +compare (GList * compared, GESTrackObject * track_object, gboolean ahead) +{ + GList *tmp; + gint64 start, duration, compared_start, compared_duration, tr_start, + tr_duration; + GESTimelineStandardTransition *tr = NULL; + GESTrack *track; + GESTimelineLayer *layer; + GESTimelineObject *object, *first_object, *second_object; + gint priority; + + object = ges_track_object_get_timeline_object (track_object); + layer = ges_timeline_object_get_layer (object); + + start = ges_track_object_get_start (track_object); + duration = ges_track_object_get_duration (track_object); + compared_start = ges_track_object_get_start (compared->data); + compared_duration = ges_track_object_get_duration (compared->data); + + if (ahead) { + for (tmp = compared->next; tmp; tmp = tmp->next) { + if GES_IS_TRACK_TRANSITION + (tmp->data) { + g_object_get (tmp->data, "start", &tr_start, "duration", &tr_duration, + NULL); + if (tr_start + tr_duration == compared_start + compared_duration) { + tr = GES_TIMELINE_STANDARD_TRANSITION + (ges_track_object_get_timeline_object (tmp->data)); + break; + } + } + } + if (compared_start + compared_duration <= start) { + if (tr) { + ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (tr)); + } + goto clean; + } else if (start + duration < compared_start + compared_duration) { + if (tr) { + ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (tr)); + } + ges_track_object_set_start (track_object, compared_start - 5); + compare (compared, track_object, FALSE); + goto clean; + } + } else { + for (tmp = compared->prev; tmp; tmp = tmp->prev) { + if GES_IS_TRACK_TRANSITION + (tmp->data) { + g_object_get (tmp->data, "start", &tr_start, "duration", &tr_duration, + NULL); + if (tr_start == compared_start) { + tr = GES_TIMELINE_STANDARD_TRANSITION + (ges_track_object_get_timeline_object (tmp->data)); + break; + } + } + } + if (start + duration <= compared_start) { + if (tr) { + ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (tr)); + } + goto clean; + } else if (start + 10 >= compared_start) { + if (tr) { + ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (tr)); + } + goto clean; + } + } + + if (tr == NULL) { + gint height; + tr = ges_timeline_standard_transition_new_for_nick ((gchar *) "crossfade"); + track = ges_track_object_get_track (track_object); + + ges_timeline_object_set_supported_formats (GES_TIMELINE_OBJECT (tr), + track->type); + + ges_timeline_layer_add_object (layer, GES_TIMELINE_OBJECT (tr)); + + if (ahead) { + first_object = ges_track_object_get_timeline_object (compared->data); + second_object = object; + } else { + second_object = ges_track_object_get_timeline_object (compared->data); + first_object = object; + } + + g_object_get (first_object, "priority", &priority, "height", &height, NULL); + g_object_set (second_object, "priority", priority + height, NULL); + g_signal_connect (first_object, "notify::height", + (GCallback) timeline_object_height_changed_cb, second_object); + } + + if (ahead) { + g_object_set (tr, "start", start, "duration", + compared_duration + compared_start - start, NULL); + } else { + g_object_set (tr, "start", compared_start, "duration", + start + duration - compared_start, NULL); + } + +clean: + g_object_unref (track_object); + g_object_unref (layer); +} + + /** * ges_timeline_layer_remove_object: * @layer: a #GESTimelineLayer @@ -317,7 +577,7 @@ ges_timeline_layer_remove_object (GESTimelineLayer * layer, /* Remove it from our list of controlled objects */ layer->priv->objects_start = - g_slist_remove (layer->priv->objects_start, object); + g_list_remove (layer->priv->objects_start, object); /* Remove our reference to the object */ g_object_unref (object); @@ -335,7 +595,7 @@ ges_timeline_layer_remove_object (GESTimelineLayer * layer, gboolean ges_timeline_layer_resync_priorities (GESTimelineLayer * layer) { - GSList *tmp; + GList *tmp; GESTimelineObject *obj; GST_DEBUG ("Resync priorities of %p", layer); @@ -376,6 +636,39 @@ ges_timeline_layer_set_priority (GESTimelineLayer * layer, guint priority) } } +/** + * ges_timeline_layer_get_auto_transition: + * @layer: a #GESTimelineLayer + * + * Get the priority of @layer within the timeline. + * + * Returns: The priority of the @layer within the timeline. + */ +gboolean +ges_timeline_layer_get_auto_transition (GESTimelineLayer * layer) +{ + g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), 0); + + return layer->priv->auto_transition; +} + +/** + * ges_timeline_layer_set_auto_transition: + * @layer: a #GESTimelineLayer + * @priority: whether the auto_transition is active + * + * Sets the layer to the given @auto_transition. See the documentation of the + * priority auto_transition for more information. + */ +void +ges_timeline_layer_set_auto_transition (GESTimelineLayer * layer, + gboolean auto_transition) +{ + g_return_if_fail (GES_IS_TIMELINE_LAYER (layer)); + + layer->priv->auto_transition = auto_transition; +} + /** * ges_timeline_layer_get_priority: * @layer: a #GESTimelineLayer @@ -407,7 +700,7 @@ GList * ges_timeline_layer_get_objects (GESTimelineLayer * layer) { GList *ret = NULL; - GSList *tmp; + GList *tmp; GESTimelineLayerClass *klass; g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), NULL); diff --git a/ges/ges-timeline-layer.h b/ges/ges-timeline-layer.h index 821fc910f9..26438f1ee1 100644 --- a/ges/ges-timeline-layer.h +++ b/ges/ges-timeline-layer.h @@ -102,7 +102,13 @@ gboolean ges_timeline_layer_remove_object (GESTimelineLayer * layer, void ges_timeline_layer_set_priority (GESTimelineLayer * layer, guint priority); -guint ges_timeline_layer_get_priority (GESTimelineLayer * layer); + +guint ges_timeline_layer_get_priority (GESTimelineLayer * layer); + +gboolean ges_timeline_layer_get_auto_transition (GESTimelineLayer * layer); + +void ges_timeline_layer_set_auto_transition (GESTimelineLayer * layer, gboolean auto_transition); + GList* ges_timeline_layer_get_objects (GESTimelineLayer * layer); G_END_DECLS diff --git a/tests/check/ges/layer.c b/tests/check/ges/layer.c index 92b5e0b8b8..e23e74ccef 100644 --- a/tests/check/ges/layer.c +++ b/tests/check/ges/layer.c @@ -292,6 +292,46 @@ GST_START_TEST (test_layer_priorities) GST_END_TEST; +GST_START_TEST (test_layer_automatic_transition) +{ + GESTimeline *timeline; + GESTimelineLayer *layer; + GESTimelineTestSource *src, *srcbis; + GList *objects = NULL, *tmp = NULL; + gboolean res = FALSE; + + ges_init (); + + timeline = ges_timeline_new_audio_video (); + layer = ges_timeline_layer_new (); + ges_timeline_add_layer (timeline, layer); + + g_object_set (layer, "auto-transition", TRUE, NULL); + src = ges_timeline_test_source_new (); + srcbis = ges_timeline_test_source_new (); + + g_object_set (srcbis, "start", (gint64) 5000, "duration", (gint64) 10000LL, + NULL); + g_object_set (src, "start", (gint64) 0, "duration", (gint64) 10000LL, NULL); + + ges_timeline_layer_add_object (layer, GES_TIMELINE_OBJECT (src)); + ges_timeline_layer_add_object (layer, GES_TIMELINE_OBJECT (srcbis)); + + objects = ges_timeline_layer_get_objects (layer); + + for (tmp = objects; tmp; tmp = tmp->next) { + if (GES_IS_TIMELINE_STANDARD_TRANSITION (tmp->data)) { + res = TRUE; + } + } + + fail_unless (res == TRUE); + printf ("zob\n"); +} + +GST_END_TEST; + + static Suite * ges_suite (void) { @@ -302,6 +342,7 @@ ges_suite (void) tcase_add_test (tc_chain, test_layer_properties); tcase_add_test (tc_chain, test_layer_priorities); + tcase_add_test (tc_chain, test_layer_automatic_transition); return s; }