diff --git a/ges/ges-clip.c b/ges/ges-clip.c index 3c755a5cca..816c4011a3 100644 --- a/ges/ges-clip.c +++ b/ges/ges-clip.c @@ -158,6 +158,74 @@ _remove_child (GESContainer * container, GESTimelineElement * element) return TRUE; } +static void +add_tlobj_to_list (gpointer key, gpointer tlobj, GList ** list) +{ + *list = g_list_prepend (*list, g_object_ref (tlobj)); +} + +static GList * +_ungroup (GESContainer * container, gboolean recursive) +{ + GList *tmp, *ret = NULL; + GESClip *tmpclip; + GESTrackType track_type; + GESTrackElement *track_element; + + gboolean first_obj = TRUE; + GESClip *clip = GES_CLIP (container); + GESTimelineElement *element = GES_TIMELINE_ELEMENT (container); + GESTimelineLayer *layer = clip->priv->layer; + GHashTable *_tracktype_clip = g_hash_table_new (g_int_hash, g_int_equal); + + /* If there is no TrackElement, just return @container in a list */ + if (GES_CONTAINER_CHILDREN (container) == NULL) { + GST_DEBUG ("No TrackElement, simply returning"); + return g_list_prepend (ret, container); + } + + /* We need a copy of the current list of tracks */ + for (tmp = GES_CONTAINER_CHILDREN (container); tmp; tmp = tmp->next) { + track_element = GES_TRACK_ELEMENT (tmp->data); + track_type = ges_track_element_get_track_type (track_element); + + tmpclip = g_hash_table_lookup (_tracktype_clip, &track_type); + if (tmpclip == NULL) { + if (G_UNLIKELY (first_obj == TRUE)) { + tmpclip = clip; + first_obj = FALSE; + } else { + tmpclip = GES_CLIP (ges_timeline_element_copy (element, FALSE)); + if (layer) { + /* Add new container to the same layer as @container */ + ges_clip_set_moving_from_layer (tmpclip, TRUE); + ges_timeline_layer_add_clip (layer, tmpclip); + ges_clip_set_moving_from_layer (tmpclip, FALSE); + } + } + + g_hash_table_insert (_tracktype_clip, &track_type, tmpclip); + ges_clip_set_supported_formats (tmpclip, track_type); + } + + /* Move trackelement to the container it is supposed to land into */ + if (tmpclip != clip) { + ges_container_remove (container, GES_TIMELINE_ELEMENT (track_element)); + ges_container_add (GES_CONTAINER (tmpclip), + GES_TIMELINE_ELEMENT (track_element)); + } + } + g_hash_table_foreach (_tracktype_clip, (GHFunc) add_tlobj_to_list, &ret); + g_hash_table_unref (_tracktype_clip); + + return ret; +} + +/**************************************************** + * * + * GObject virtual methods implementation * + * * + ****************************************************/ static void ges_clip_get_property (GObject * object, guint property_id, GValue * value, GParamSpec * pspec) @@ -241,6 +309,7 @@ ges_clip_class_init (GESClipClass * klass) container_class->get_priorty_range = _get_priorty_range; container_class->add_child = _add_child; container_class->remove_child = _remove_child; + container_class->ungroup = _ungroup; klass->need_fill_track = TRUE; } diff --git a/ges/ges-container.c b/ges/ges-container.c index 4fc8428bf1..ba235c5097 100644 --- a/ges/ges-container.c +++ b/ges/ges-container.c @@ -389,6 +389,7 @@ ges_container_class_init (GESContainerClass * klass) /* No default implementations */ klass->remove_child = NULL; klass->add_child = NULL; + klass->ungroup = NULL; } static void @@ -669,3 +670,36 @@ ges_container_get_children (GESContainer * container) return g_list_copy_deep (container->children, (GCopyFunc) gst_object_ref, NULL); } + +/** + * ges_container_ungroup + * @container: (transfer full): The #GESContainer to ungroup + * @recursive: Wether to recursively ungroup @container + * + * Ungroups the #GESTimelineElement contained in this GESContainer, + * creating new #GESContainer containing those #GESTimelineElement + * properly apropriately. + * + * Returns: (transfer container) (element-type GESContainer): The list of + * #GESContainer resulting from the ungrouping operation + * The user is responsible for unreffing the contained objects + * and freeing the list. + */ +GList * +ges_container_ungroup (GESContainer * container, gboolean recursive) +{ + GESContainerClass *klass; + + g_return_val_if_fail (GES_IS_CONTAINER (container), NULL); + + GST_DEBUG_OBJECT (container, "Ungrouping container %s recursively", + recursive ? "" : "not"); + + klass = GES_CONTAINER_GET_CLASS (container); + if (klass->ungroup == NULL) { + GST_INFO_OBJECT (container, "No ungoup virtual method, doint nothing"); + return NULL; + } + + return klass->ungroup (container, recursive); +} diff --git a/ges/ges-container.h b/ges/ges-container.h index b93d2d7290..a44d6ad9f7 100644 --- a/ges/ges-container.h +++ b/ges/ges-container.h @@ -89,6 +89,8 @@ struct _GESContainer * @remove_child: Virtual method to remove a child * @add_child: Virtual method to add a child * @get_priorty_range: Returns the range of possible priority in which the children can be in. + * @ungroup: Ungroups the #GESTimelineElement contained in this #GESContainer, creating new + * #GESContainer containing those #GESTimelineElement apropriately. */ struct _GESContainerClass { @@ -102,6 +104,7 @@ struct _GESContainerClass gboolean (*add_child) (GESContainer *container, GESTimelineElement *element); gboolean (*remove_child) (GESContainer *container, GESTimelineElement *element); void (*get_priorty_range) (GESContainer *container, guint32 *min_prio, guint32 *max_prio); + GList* (*ungroup) (GESContainer *container, gboolean recursive); /*< private >*/ @@ -115,6 +118,7 @@ GType ges_container_get_type (void); GList* ges_container_get_children (GESContainer *container); gboolean ges_container_add (GESContainer *container, GESTimelineElement *child); gboolean ges_container_remove (GESContainer *container, GESTimelineElement *child); +GList * ges_container_ungroup (GESContainer * container, gboolean recursive); G_END_DECLS #endif /* _GES_CONTAINER */ diff --git a/tests/check/ges/clip.c b/tests/check/ges/clip.c index 41449ea61f..a5447576a0 100644 --- a/tests/check/ges/clip.c +++ b/tests/check/ges/clip.c @@ -177,6 +177,89 @@ GST_START_TEST (test_split_object) GST_END_TEST; +GST_START_TEST (test_clip_ungroup) +{ + GESAsset *asset; + GESTimeline *timeline; + GESClip *clip, *clip2; + GList *containers, *tmp; + GESTimelineLayer *layer; + GESTrack *audio_track, *video_track; + + ges_init (); + + timeline = ges_timeline_new (); + layer = ges_timeline_layer_new (); + audio_track = ges_track_audio_raw_new (); + video_track = ges_track_video_raw_new (); + + fail_unless (ges_timeline_add_track (timeline, audio_track)); + fail_unless (ges_timeline_add_track (timeline, video_track)); + fail_unless (ges_timeline_add_layer (timeline, layer)); + + asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL); + assert_is_type (asset, GES_TYPE_ASSET); + + clip = ges_timeline_layer_add_asset (layer, asset, 0, 0, 10, 1, + GES_TRACK_TYPE_UNKNOWN); + ASSERT_OBJECT_REFCOUNT (clip, "1 layer", 1); + assert_equals_uint64 (_START (clip), 0); + assert_equals_uint64 (_INPOINT (clip), 0); + assert_equals_uint64 (_DURATION (clip), 10); + assert_equals_int (g_list_length (GES_CONTAINER_CHILDREN (clip)), 2); + + containers = ges_container_ungroup (GES_CONTAINER (clip), FALSE); + assert_equals_int (g_list_length (containers), 2); + fail_unless (clip == containers->data); + assert_equals_int (g_list_length (GES_CONTAINER_CHILDREN (clip)), 1); + assert_equals_uint64 (_START (clip), 0); + assert_equals_uint64 (_INPOINT (clip), 0); + assert_equals_uint64 (_DURATION (clip), 10); + ASSERT_OBJECT_REFCOUNT (clip, "1 for the layer + 1 in containers list", 2); + + clip2 = containers->next->data; + fail_if (clip2 == clip); + assert_equals_int (g_list_length (GES_CONTAINER_CHILDREN (clip2)), 1); + assert_equals_uint64 (_START (clip2), 0); + assert_equals_uint64 (_INPOINT (clip2), 0); + assert_equals_uint64 (_DURATION (clip2), 10); + ASSERT_OBJECT_REFCOUNT (clip2, "1 for the layer + 1 in containers list", 2); + g_list_free_full (containers, gst_object_unref); + + tmp = ges_track_get_elements (audio_track); + assert_equals_int (g_list_length (tmp), 1); + ASSERT_OBJECT_REFCOUNT (tmp->data, "1 for the track + 1 for the container " + "+ 1 for the timeline + 1 in tmp list", 4); + assert_equals_int (ges_track_element_get_track_type (tmp->data), + GES_TRACK_TYPE_AUDIO); + assert_equals_int (ges_clip_get_supported_formats (GES_CLIP + (ges_timeline_element_get_parent (tmp->data))), GES_TRACK_TYPE_AUDIO); + g_list_free_full (tmp, gst_object_unref); + tmp = ges_track_get_elements (video_track); + assert_equals_int (g_list_length (tmp), 1); + ASSERT_OBJECT_REFCOUNT (tmp->data, "1 for the track + 1 for the container " + "+ 1 for the timeline + 1 in tmp list", 4); + assert_equals_int (ges_track_element_get_track_type (tmp->data), + GES_TRACK_TYPE_VIDEO); + assert_equals_int (ges_clip_get_supported_formats (GES_CLIP + (ges_timeline_element_get_parent (tmp->data))), GES_TRACK_TYPE_VIDEO); + g_list_free_full (tmp, gst_object_unref); + + ges_timeline_element_set_start (GES_TIMELINE_ELEMENT (clip), 10); + assert_equals_int (g_list_length (GES_CONTAINER_CHILDREN (clip)), 1); + assert_equals_uint64 (_START (clip), 10); + assert_equals_uint64 (_INPOINT (clip), 0); + assert_equals_uint64 (_DURATION (clip), 10); + assert_equals_int (g_list_length (GES_CONTAINER_CHILDREN (clip2)), 1); + assert_equals_uint64 (_START (clip2), 0); + assert_equals_uint64 (_INPOINT (clip2), 0); + assert_equals_uint64 (_DURATION (clip2), 10); + + gst_object_unref (timeline); +} + +GST_END_TEST; + static Suite * ges_suite (void) { @@ -187,6 +270,7 @@ ges_suite (void) tcase_add_test (tc_chain, test_object_properties); tcase_add_test (tc_chain, test_split_object); + tcase_add_test (tc_chain, test_clip_ungroup); return s; }