1 /* GStreamer Editing Services
2 * Copyright (C) <2013> Thibault Saunier <thibault.saunier@collabora.com>
3 * <2013> Collabora Ltd.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
22 * SECTION:gescontainer
23 * @title: GESContainer
24 * @short_description: Base Class for objects responsible for controlling other
25 * GESTimelineElement-s
31 #include "ges-container.h"
33 #include "ges-internal.h"
37 GST_DEBUG_CATEGORY_STATIC (ges_container_debug);
38 #undef GST_CAT_DEFAULT
39 #define GST_CAT_DEFAULT ges_container_debug
41 /* Mapping of relationship between a Container and the TimelineElements
44 * NOTE : Does it make sense to make it public in the future ?
48 GESTimelineElement *child;
50 GstClockTime start_offset;
51 GstClockTime duration_offset;
52 GstClockTime inpoint_offset;
53 gint32 priority_offset;
56 guint duration_notifyid;
57 guint inpoint_notifyid;
67 static guint ges_container_signals[LAST_SIGNAL] = { 0 };
69 struct _GESContainerPrivate
75 /* Set to TRUE when the container is doing updates of track object
76 * properties so we don't end up in infinite property update loops
81 /* List of GESTimelineElement being in the "child-added" signal
83 GList *adding_children;
85 GList *copied_children;
95 static GParamSpec *properties[PROP_LAST];
97 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GESContainer, ges_container,
98 GES_TYPE_TIMELINE_ELEMENT);
100 /************************
102 ************************/
104 _free_mapping (ChildMapping * mapping)
106 GESTimelineElement *child = mapping->child;
108 /* Disconnect all notify listeners */
109 if (mapping->start_notifyid)
110 g_signal_handler_disconnect (child, mapping->start_notifyid);
111 if (mapping->duration_notifyid)
112 g_signal_handler_disconnect (child, mapping->duration_notifyid);
113 if (mapping->inpoint_notifyid)
114 g_signal_handler_disconnect (child, mapping->inpoint_notifyid);
116 ges_timeline_element_set_parent (child, NULL);
117 g_slice_free (ChildMapping, mapping);
121 compare_grouping_prio (GType * a, GType * b)
124 GObjectClass *aclass = g_type_class_ref (*a);
125 GObjectClass *bclass = g_type_class_ref (*b);
127 /* We want higher prios to be first */
128 if (GES_CONTAINER_CLASS (aclass)->grouping_priority <
129 GES_CONTAINER_CLASS (bclass)->grouping_priority)
131 else if (GES_CONTAINER_CLASS (aclass)->grouping_priority >
132 GES_CONTAINER_CLASS (bclass)->grouping_priority)
135 g_type_class_unref (aclass);
136 g_type_class_unref (bclass);
141 _resync_start_offsets (GESTimelineElement * child,
142 ChildMapping * map, GESContainer * container)
144 map->start_offset = _START (container) - _START (child);
147 /*****************************************************
149 * GESTimelineElement virtual methods implementation *
151 *****************************************************/
153 _set_start (GESTimelineElement * element, GstClockTime start)
157 GESContainer *container = GES_CONTAINER (element);
158 GESContainerPrivate *priv = container->priv;
160 GST_DEBUG_OBJECT (element, "Updating children offsets, (initiated_move: %"
161 GST_PTR_FORMAT ")", container->initiated_move);
163 for (tmp = container->children; tmp; tmp = g_list_next (tmp)) {
164 GESTimelineElement *child = (GESTimelineElement *) tmp->data;
166 map = g_hash_table_lookup (priv->mappings, child);
167 map->start_offset = start - _START (child);
169 container->children_control_mode = GES_CHILDREN_UPDATE;
175 _set_inpoint (GESTimelineElement * element, GstClockTime inpoint)
178 GESContainer *container = GES_CONTAINER (element);
180 for (tmp = container->children; tmp; tmp = g_list_next (tmp)) {
181 GESTimelineElement *child = (GESTimelineElement *) tmp->data;
182 ChildMapping *map = g_hash_table_lookup (container->priv->mappings, child);
184 map->inpoint_offset = inpoint - _INPOINT (child);
191 _set_duration (GESTimelineElement * element, GstClockTime duration)
194 GESContainer *container = GES_CONTAINER (element);
195 GESContainerPrivate *priv = container->priv;
197 for (tmp = container->children; tmp; tmp = g_list_next (tmp)) {
198 GESTimelineElement *child = (GESTimelineElement *) tmp->data;
199 ChildMapping *map = g_hash_table_lookup (priv->mappings, child);
201 map->duration_offset = duration - _DURATION (child);
208 _ges_container_add_child_properties (GESContainer * container,
209 GESTimelineElement * child)
213 GParamSpec **child_props =
214 ges_timeline_element_list_children_properties (child,
217 for (i = 0; i < n_props; i++) {
219 gchar *prop_name = g_strdup_printf ("%s::%s",
220 g_type_name (child_props[i]->owner_type),
221 child_props[i]->name);
223 if (ges_timeline_element_lookup_child (child, prop_name, &prop_child, NULL)) {
224 ges_timeline_element_add_child_property (GES_TIMELINE_ELEMENT (container),
225 child_props[i], prop_child);
226 gst_object_unref (prop_child);
230 g_param_spec_unref (child_props[i]);
233 g_free (child_props);
237 _ges_container_remove_child_properties (GESContainer * container,
238 GESTimelineElement * child)
242 GParamSpec **child_props =
243 ges_timeline_element_list_children_properties (child,
246 for (i = 0; i < n_props; i++) {
248 gchar *prop_name = g_strdup_printf ("%s::%s",
249 g_type_name (child_props[i]->owner_type),
250 child_props[i]->name);
252 if (ges_timeline_element_lookup_child (child, prop_name, &prop_child, NULL)) {
253 ges_timeline_element_remove_child_property (GES_TIMELINE_ELEMENT
254 (container), child_props[i]);
255 gst_object_unref (prop_child);
260 g_param_spec_unref (child_props[i]);
263 g_free (child_props);
267 _list_children_properties (GESTimelineElement * self, guint * n_properties)
271 for (tmp = GES_CONTAINER_CHILDREN (self); tmp; tmp = tmp->next)
272 _ges_container_add_child_properties (GES_CONTAINER (self), tmp->data);
275 GES_TIMELINE_ELEMENT_CLASS
276 (ges_container_parent_class)->list_children_properties (self,
281 _lookup_child (GESTimelineElement * self, const gchar * prop_name,
282 GObject ** child, GParamSpec ** pspec)
286 /* FIXME Implement a syntax to precisely get properties by path */
287 for (tmp = GES_CONTAINER_CHILDREN (self); tmp; tmp = tmp->next) {
288 if (ges_timeline_element_lookup_child (tmp->data, prop_name, child, pspec))
296 _get_track_types (GESTimelineElement * object)
298 GESTrackType types = GES_TRACK_TYPE_UNKNOWN;
299 GList *tmp, *children = ges_container_get_children (GES_CONTAINER (object),
302 for (tmp = children; tmp; tmp = tmp->next) {
303 if (GES_IS_TRACK_ELEMENT (tmp->data)) {
304 types |= ges_timeline_element_get_track_types (tmp->data);
308 g_list_free_full (children, gst_object_unref);
310 return types ^ GES_TRACK_TYPE_UNKNOWN;
314 _deep_copy (GESTimelineElement * element, GESTimelineElement * copy)
317 GESContainer *self = GES_CONTAINER (element), *ccopy = GES_CONTAINER (copy);
319 for (tmp = GES_CONTAINER_CHILDREN (element); tmp; tmp = tmp->next) {
323 g_slice_dup (ChildMapping, g_hash_table_lookup (self->priv->mappings,
325 map->child = ges_timeline_element_copy (tmp->data, TRUE);
326 map->start_notifyid = 0;
327 map->inpoint_notifyid = 0;
328 map->duration_notifyid = 0;
330 ccopy->priv->copied_children = g_list_prepend (ccopy->priv->copied_children,
335 static GESTimelineElement *
336 _paste (GESTimelineElement * element, GESTimelineElement * ref,
337 GstClockTime paste_position)
341 GESContainer *ncontainer =
342 GES_CONTAINER (ges_timeline_element_copy (element, FALSE));
343 GESContainer *self = GES_CONTAINER (element);
345 for (tmp = self->priv->copied_children; tmp; tmp = tmp->next) {
346 GESTimelineElement *nchild;
350 ges_timeline_element_paste (map->child,
351 paste_position - map->start_offset);
352 ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (ncontainer),
353 GES_TIMELINE_ELEMENT_TIMELINE (ref));
354 ges_container_add (ncontainer, nchild);
357 return GES_TIMELINE_ELEMENT (ncontainer);
361 /******************************************
363 * GObject virtual methods implementation *
365 ******************************************/
367 _dispose (GObject * object)
370 GESContainer *self = GES_CONTAINER (object);
373 _ges_container_sort_children (self);
374 children = ges_container_get_children (self, FALSE);
376 for (tmp = g_list_last (children); tmp; tmp = tmp->prev)
377 ges_container_remove (self, tmp->data);
379 g_list_free_full (children, gst_object_unref);
380 self->children = NULL;
382 G_OBJECT_CLASS (ges_container_parent_class)->dispose (object);
386 _finalize (GObject * object)
388 GESContainer *self = GES_CONTAINER (object);
390 g_list_free_full (self->priv->copied_children,
391 (GDestroyNotify) _free_mapping);
393 if (self->priv->mappings)
394 g_hash_table_destroy (self->priv->mappings);
396 G_OBJECT_CLASS (ges_container_parent_class)->finalize (object);
400 _get_property (GObject * container, guint property_id,
401 GValue * value, GParamSpec * pspec)
403 GESContainer *tobj = GES_CONTAINER (container);
405 switch (property_id) {
407 g_value_set_uint (value, tobj->height);
410 G_OBJECT_WARN_INVALID_PROPERTY_ID (container, property_id, pspec);
415 _set_property (GObject * container, guint property_id,
416 const GValue * value, GParamSpec * pspec)
418 switch (property_id) {
420 G_OBJECT_WARN_INVALID_PROPERTY_ID (container, property_id, pspec);
425 ges_container_class_init (GESContainerClass * klass)
427 GObjectClass *object_class = G_OBJECT_CLASS (klass);
428 GESTimelineElementClass *element_class = GES_TIMELINE_ELEMENT_CLASS (klass);
430 GST_DEBUG_CATEGORY_INIT (ges_container_debug, "gescontainer",
431 GST_DEBUG_FG_YELLOW, "ges container");
433 object_class->get_property = _get_property;
434 object_class->set_property = _set_property;
435 object_class->dispose = _dispose;
436 object_class->finalize = _finalize;
439 * GESContainer:height:
441 * The span of priorities which this container occupies.
443 properties[PROP_HEIGHT] = g_param_spec_uint ("height", "Height",
444 "The span of priorities this container occupies", 0, G_MAXUINT, 1,
446 g_object_class_install_property (object_class, PROP_HEIGHT,
447 properties[PROP_HEIGHT]);
450 * GESContainer::child-added:
451 * @container: the #GESContainer
452 * @element: the #GESTimelineElement that was added.
454 * Will be emitted after a child was added to @container.
455 * Usually you should connect with #g_signal_connect_after
456 * as in the first emission stage, the signal emission might
457 * get stopped internally.
459 ges_container_signals[CHILD_ADDED_SIGNAL] =
460 g_signal_new ("child-added", G_TYPE_FROM_CLASS (klass),
461 G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESContainerClass, child_added),
462 NULL, NULL, g_cclosure_marshal_generic,
463 G_TYPE_NONE, 1, GES_TYPE_TIMELINE_ELEMENT);
466 * GESContainer::child-removed:
467 * @container: the #GESContainer
468 * @element: the #GESTimelineElement that was removed.
470 * Will be emitted after a child was removed from @container.
472 ges_container_signals[CHILD_REMOVED_SIGNAL] =
473 g_signal_new ("child-removed", G_TYPE_FROM_CLASS (klass),
474 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GESContainerClass, child_removed),
475 NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
476 GES_TYPE_TIMELINE_ELEMENT);
479 element_class->set_start = _set_start;
480 element_class->set_duration = _set_duration;
481 element_class->set_inpoint = _set_inpoint;
482 element_class->list_children_properties = _list_children_properties;
483 element_class->lookup_child = _lookup_child;
484 element_class->get_track_types = _get_track_types;
485 element_class->paste = _paste;
486 element_class->deep_copy = _deep_copy;
488 /* No default implementations */
489 klass->remove_child = NULL;
490 klass->add_child = NULL;
491 klass->ungroup = NULL;
493 klass->grouping_priority = 0;
498 ges_container_init (GESContainer * self)
500 self->priv = ges_container_get_instance_private (self);
502 /* FIXME, check why default was GST_SECOND? (before the existend of
505 * _DURATION (self) = GST_SECOND; */
506 self->height = 1; /* FIXME Why 1 and not 0? */
507 self->children = NULL;
509 self->priv->mappings = g_hash_table_new_full (g_direct_hash, g_direct_equal,
510 NULL, (GDestroyNotify) _free_mapping);
513 /**********************************************
515 * Property notifications from Children *
517 **********************************************/
519 _child_start_changed_cb (GESTimelineElement * child,
520 GParamSpec * arg G_GNUC_UNUSED, GESContainer * container)
525 GESContainerPrivate *priv = container->priv;
526 GESTimelineElement *element = GES_TIMELINE_ELEMENT (container);
528 map = g_hash_table_lookup (priv->mappings, child);
531 switch (container->children_control_mode) {
532 case GES_CHILDREN_IGNORE_NOTIFIES:
534 case GES_CHILDREN_UPDATE_ALL_VALUES:
535 _ges_container_sort_children (container);
536 start = container->children ?
537 _START (container->children->data) : _START (container);
539 if (start != _START (container)) {
540 _DURATION (container) = _END (container) - start;
541 _START (container) = start;
543 GST_DEBUG_OBJECT (container, "Child move made us "
544 "move to %" GST_TIME_FORMAT, GST_TIME_ARGS (_START (container)));
546 g_object_notify (G_OBJECT (container), "start");
550 case GES_CHILDREN_UPDATE_OFFSETS:
551 map->start_offset = _START (container) - _START (child);
554 case GES_CHILDREN_UPDATE:
555 /* We update all the children calling our set_start method */
556 container->initiated_move = child;
557 _set_start0 (element, _START (child) + map->start_offset);
558 container->initiated_move = NULL;
566 _child_inpoint_changed_cb (GESTimelineElement * child,
567 GParamSpec * arg G_GNUC_UNUSED, GESContainer * container)
571 GESContainerPrivate *priv = container->priv;
572 GESTimelineElement *element = GES_TIMELINE_ELEMENT (container);
574 if (container->children_control_mode == GES_CHILDREN_IGNORE_NOTIFIES)
577 map = g_hash_table_lookup (priv->mappings, child);
580 if (container->children_control_mode == GES_CHILDREN_UPDATE_OFFSETS) {
581 map->inpoint_offset = _START (container) - _START (child);
586 /* We update all the children calling our set_inpoint method */
587 container->initiated_move = child;
588 _set_inpoint0 (element, _INPOINT (child) + map->inpoint_offset);
589 container->initiated_move = NULL;
593 _child_duration_changed_cb (GESTimelineElement * child,
594 GParamSpec * arg G_GNUC_UNUSED, GESContainer * container)
599 GstClockTime end = 0;
600 GESContainerPrivate *priv = container->priv;
601 GESTimelineElement *element = GES_TIMELINE_ELEMENT (container);
603 if (container->children_control_mode == GES_CHILDREN_IGNORE_NOTIFIES)
606 map = g_hash_table_lookup (priv->mappings, child);
609 switch (container->children_control_mode) {
610 case GES_CHILDREN_IGNORE_NOTIFIES:
612 case GES_CHILDREN_UPDATE_ALL_VALUES:
613 _ges_container_sort_children_by_end (container);
615 for (tmp = container->children; tmp; tmp = tmp->next)
616 end = MAX (end, _END (tmp->data));
618 if (end != _END (container)) {
619 _DURATION (container) = end - _START (container);
620 g_object_notify (G_OBJECT (container), "duration");
623 case GES_CHILDREN_UPDATE_OFFSETS:
624 map->inpoint_offset = _START (container) - _START (child);
626 case GES_CHILDREN_UPDATE:
627 /* We update all the children calling our set_duration method */
628 container->initiated_move = child;
629 _set_duration0 (element, _DURATION (child) + map->duration_offset);
630 container->initiated_move = NULL;
637 /****************************************************
639 * Internal methods implementation *
641 ****************************************************/
644 _ges_container_sort_children (GESContainer * container)
646 container->children = g_list_sort (container->children,
647 (GCompareFunc) element_start_compare);
651 _ges_container_sort_children_by_end (GESContainer * container)
653 container->children = g_list_sort (container->children,
654 (GCompareFunc) element_end_compare);
658 _ges_container_set_height (GESContainer * container, guint32 height)
660 if (container->height != height) {
661 container->height = height;
662 GST_DEBUG_OBJECT (container, "Updating height %i", container->height);
663 g_object_notify (G_OBJECT (container), "height");
668 _ges_container_get_priority_offset (GESContainer * container,
669 GESTimelineElement * elem)
671 ChildMapping *map = g_hash_table_lookup (container->priv->mappings, elem);
673 g_return_val_if_fail (map, 0);
675 return map->priority_offset;
679 _ges_container_set_priority_offset (GESContainer * container,
680 GESTimelineElement * elem, gint32 priority_offset)
682 ChildMapping *map = g_hash_table_lookup (container->priv->mappings, elem);
684 g_return_if_fail (map);
686 map->priority_offset = priority_offset;
689 /**********************************************
691 * API implementation *
693 **********************************************/
697 * @container: a #GESContainer
698 * @child: the #GESTimelineElement
700 * Add the #GESTimelineElement to the container.
702 * Returns: %TRUE on success, %FALSE on failure.
705 ges_container_add (GESContainer * container, GESTimelineElement * child)
707 ChildMapping *mapping;
708 gboolean notify_start = FALSE;
709 GESContainerClass *class;
710 GESContainerPrivate *priv;
712 g_return_val_if_fail (GES_IS_CONTAINER (container), FALSE);
713 g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (child), FALSE);
714 g_return_val_if_fail (GES_TIMELINE_ELEMENT_PARENT (child) == NULL, FALSE);
716 class = GES_CONTAINER_GET_CLASS (container);
717 priv = container->priv;
719 GST_DEBUG_OBJECT (container, "adding timeline element %" GST_PTR_FORMAT,
722 container->children_control_mode = GES_CHILDREN_IGNORE_NOTIFIES;
723 if (class->add_child) {
724 if (class->add_child (container, child) == FALSE) {
725 container->children_control_mode = GES_CHILDREN_UPDATE;
726 GST_WARNING_OBJECT (container, "Erreur adding child %p", child);
730 container->children_control_mode = GES_CHILDREN_UPDATE;
732 if (_START (container) > _START (child)) {
733 _START (container) = _START (child);
735 g_hash_table_foreach (priv->mappings, (GHFunc) _resync_start_offsets,
740 mapping = g_slice_new0 (ChildMapping);
741 mapping->child = gst_object_ref (child);
742 mapping->start_offset = _START (container) - _START (child);
743 mapping->duration_offset = _DURATION (container) - _DURATION (child);
744 mapping->inpoint_offset = _INPOINT (container) - _INPOINT (child);
746 g_hash_table_insert (priv->mappings, child, mapping);
748 container->children = g_list_prepend (container->children, child);
750 _ges_container_sort_children (container);
752 /* Listen to all property changes */
753 mapping->start_notifyid =
754 g_signal_connect (G_OBJECT (child), "notify::start",
755 G_CALLBACK (_child_start_changed_cb), container);
756 mapping->duration_notifyid =
757 g_signal_connect (G_OBJECT (child), "notify::duration",
758 G_CALLBACK (_child_duration_changed_cb), container);
759 mapping->inpoint_notifyid =
760 g_signal_connect (G_OBJECT (child), "notify::in-point",
761 G_CALLBACK (_child_inpoint_changed_cb), container);
763 if (ges_timeline_element_set_parent (child, GES_TIMELINE_ELEMENT (container))
766 g_hash_table_remove (priv->mappings, child);
767 container->children = g_list_remove (container->children, child);
768 _ges_container_sort_children (container);
773 _ges_container_add_child_properties (container, child);
775 priv->adding_children = g_list_prepend (priv->adding_children, child);
776 g_signal_emit (container, ges_container_signals[CHILD_ADDED_SIGNAL], 0,
778 priv->adding_children = g_list_remove (priv->adding_children, child);
781 g_object_notify (G_OBJECT (container), "start");
787 * ges_container_remove:
788 * @container: a #GESContainer
789 * @child: the #GESTimelineElement to release
791 * Release the @child from the control of @container.
793 * Returns: %TRUE if the @child was properly released, else %FALSE.
796 ges_container_remove (GESContainer * container, GESTimelineElement * child)
798 GESContainerClass *klass;
799 GESContainerPrivate *priv;
801 g_return_val_if_fail (GES_IS_CONTAINER (container), FALSE);
802 g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (child), FALSE);
804 GST_DEBUG_OBJECT (container, "removing child: %" GST_PTR_FORMAT, child);
806 klass = GES_CONTAINER_GET_CLASS (container);
807 priv = container->priv;
809 if (!(g_hash_table_lookup (priv->mappings, child))) {
810 GST_WARNING_OBJECT (container, "Element isn't controlled by this "
815 if (klass->remove_child) {
816 if (klass->remove_child (container, child) == FALSE)
820 container->children = g_list_remove (container->children, child);
821 /* Let it live removing from our mappings */
822 g_hash_table_remove (priv->mappings, child);
824 _ges_container_remove_child_properties (container, child);
826 if (!g_list_find (container->priv->adding_children, child)) {
827 g_signal_emit (container, ges_container_signals[CHILD_REMOVED_SIGNAL], 0,
830 GST_INFO_OBJECT (container, "Not emitting 'child-removed' signal as child"
831 " removal happend during 'child-added' signal emission");
833 gst_object_unref (child);
839 _get_children_recursively (GESContainer * container, GList ** children)
844 g_list_concat (*children, g_list_copy_deep (container->children,
845 (GCopyFunc) gst_object_ref, NULL));
847 for (tmp = container->children; tmp; tmp = tmp->next) {
848 GESTimelineElement *element = tmp->data;
850 if (GES_IS_CONTAINER (element))
851 _get_children_recursively (tmp->data, children);
856 * ges_container_get_children:
857 * @container: a #GESContainer
858 * @recursive: Whether to recursively get children in @container
860 * Get the list of #GESTimelineElement contained in @container
861 * The user is responsible for unreffing the contained objects
862 * and freeing the list.
864 * Returns: (transfer full) (element-type GESTimelineElement): The list of
865 * timeline element contained in @container.
868 ges_container_get_children (GESContainer * container, gboolean recursive)
870 GList *children = NULL;
872 g_return_val_if_fail (GES_IS_CONTAINER (container), NULL);
875 return g_list_copy_deep (container->children, (GCopyFunc) gst_object_ref,
878 _get_children_recursively (container, &children);
883 * ges_container_ungroup:
884 * @container: (transfer full): The #GESContainer to ungroup
885 * @recursive: Wether to recursively ungroup @container
887 * Ungroups the #GESTimelineElement contained in this GESContainer,
888 * creating new #GESContainer containing those #GESTimelineElement
891 * Returns: (transfer full) (element-type GESContainer): The list of
892 * #GESContainer resulting from the ungrouping operation
893 * The user is responsible for unreffing the contained objects
894 * and freeing the list.
897 ges_container_ungroup (GESContainer * container, gboolean recursive)
899 GESContainerClass *klass;
901 g_return_val_if_fail (GES_IS_CONTAINER (container), NULL);
903 GST_DEBUG_OBJECT (container, "Ungrouping container %s recursively",
904 recursive ? "" : "not");
906 klass = GES_CONTAINER_GET_CLASS (container);
907 if (klass->ungroup == NULL) {
908 GST_INFO_OBJECT (container, "No ungoup virtual method, doint nothing");
912 return klass->ungroup (container, recursive);
916 * ges_container_group:
917 * @containers: (transfer none)(element-type GESContainer) (allow-none): The
918 * #GESContainer to group, they must all be in a same #GESTimeline
920 * Groups the #GESContainer-s provided in @containers. It creates a subclass
921 * of #GESContainer, depending on the containers provided in @containers.
922 * Basically, if all the containers in @containers should be contained in a same
923 * clip (all the #GESTrackElement they contain have the exact same
924 * start/inpoint/duration and are in the same layer), it will create a #GESClip
925 * otherwise a #GESGroup will be created
927 * Returns: (transfer none): The #GESContainer (subclass) resulting of the
931 ges_container_group (GList * containers)
935 GType *children_types;
936 GESTimelineElement *element;
937 GObjectClass *clip_class;
940 GESContainer *ret = NULL;
941 GESTimeline *timeline = NULL;
944 element = GES_TIMELINE_ELEMENT (containers->data);
945 timeline = GES_TIMELINE_ELEMENT_TIMELINE (element);
946 g_return_val_if_fail (timeline, NULL);
949 if (g_list_length (containers) == 1)
950 return containers->data;
952 for (tmp = containers; tmp; tmp = tmp->next) {
953 g_return_val_if_fail (GES_IS_CONTAINER (tmp->data), NULL);
954 g_return_val_if_fail (GES_TIMELINE_ELEMENT_PARENT (tmp->data) == NULL,
956 g_return_val_if_fail (GES_TIMELINE_ELEMENT_TIMELINE (tmp->data) == timeline,
960 children_types = g_type_children (GES_TYPE_CONTAINER, &n_children);
961 g_qsort_with_data (children_types, n_children, sizeof (GType),
962 (GCompareDataFunc) compare_grouping_prio, NULL);
964 for (i = 0; i < n_children; i++) {
965 clip_class = g_type_class_peek (children_types[i]);
966 ret = GES_CONTAINER_CLASS (clip_class)->group (containers);
972 g_free (children_types);
977 * ges_container_edit:
978 * @container: the #GESClip to edit
979 * @layers: (element-type GESLayer): The layers you want the edit to
980 * happen in, %NULL means that the edition is done in all the
981 * #GESLayers contained in the current timeline.
982 * @new_layer_priority: The priority of the layer @container should land in.
983 * If the layer you're trying to move the container to doesn't exist, it will
984 * be created automatically. -1 means no move.
985 * @mode: The #GESEditMode in which the editition will happen.
986 * @edge: The #GESEdge the edit should happen on.
987 * @position: The position at which to edit @container (in nanosecond)
989 * Edit @container in the different exisiting #GESEditMode modes. In the case of
990 * slide, and roll, you need to specify a #GESEdge
992 * Returns: %TRUE if the container as been edited properly, %FALSE if an error
996 ges_container_edit (GESContainer * container, GList * layers,
997 gint new_layer_priority, GESEditMode mode, GESEdge edge, guint64 position)
999 g_return_val_if_fail (GES_IS_CONTAINER (container), FALSE);
1001 if (G_UNLIKELY (GES_CONTAINER_GET_CLASS (container)->edit == NULL)) {
1002 GST_WARNING_OBJECT (container, "No edit vmethod implementation");
1006 return GES_CONTAINER_GET_CLASS (container)->edit (container, layers,
1007 new_layer_priority, mode, edge, position);