container: Add a way to ungroup a GESContainer into several GESContainers
authorThibault Saunier <thibault.saunier@collabora.com>
Fri, 1 Mar 2013 23:25:17 +0000 (20:25 -0300)
committerThibault Saunier <thibault.saunier@collabora.com>
Fri, 15 Mar 2013 03:13:09 +0000 (00:13 -0300)
  + Add simple unit test

API:
  GESContainerClass::ungroup vmethod
  ges_container_ungroup

ges/ges-clip.c
ges/ges-container.c
ges/ges-container.h
tests/check/ges/clip.c

index 3c755a5..816c401 100644 (file)
@@ -159,6 +159,74 @@ _remove_child (GESContainer * container, GESTimelineElement * element)
 }
 
 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;
 }
index 4fc8428..ba235c5 100644 (file)
@@ -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);
+}
index b93d2d7..a44d6ad 100644 (file)
@@ -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 */
index 41449ea..a544757 100644 (file)
@@ -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;
 }