summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThibault Saunier <thibault.saunier@collabora.com>2013-06-26 17:08:57 -0400
committerThibault Saunier <thibault.saunier@collabora.com>2013-07-09 16:47:00 -0400
commita933d9540d4677c055d6b1e9a9fe6e9821d59478 (patch)
tree961d1a4dfccb90716400232b08a95e0521c6d754
parentd3e3871440e7cfa2b6e6a0745c058c8fb7161a45 (diff)
ges: Implement a GESGroup class, subclass of GESContainer
The GESGroup class is used to group various GESContainer together, it can contain either GESClips or GESGroup or both.
-rw-r--r--docs/libs/ges-docs.sgml1
-rw-r--r--docs/libs/ges-sections.txt17
-rw-r--r--ges/Makefile.am2
-rw-r--r--ges/ges-clip.c4
-rw-r--r--ges/ges-container.c19
-rw-r--r--ges/ges-container.h1
-rw-r--r--ges/ges-group.c550
-rw-r--r--ges/ges-group.h56
-rw-r--r--ges/ges-internal.h8
-rw-r--r--ges/ges-timeline-element.c11
-rw-r--r--ges/ges-timeline.c202
-rw-r--r--ges/ges-types.h3
-rw-r--r--ges/ges.c2
-rw-r--r--ges/ges.h1
-rw-r--r--tests/check/Makefile.am1
-rw-r--r--tests/check/ges/basic.c4
-rw-r--r--tests/check/ges/clip.c6
-rw-r--r--tests/check/ges/group.c500
-rw-r--r--tests/check/ges/test-utils.h12
-rw-r--r--tests/check/ges/timelineedition.c336
20 files changed, 1619 insertions, 117 deletions
diff --git a/docs/libs/ges-docs.sgml b/docs/libs/ges-docs.sgml
index 9aaae74..b5f8c00 100644
--- a/docs/libs/ges-docs.sgml
+++ b/docs/libs/ges-docs.sgml
@@ -61,6 +61,7 @@ platform as well as Windows. It is released under the GNU Library General Public
<xi:include href="xml/ges-text-overlay-clip.xml"/>
<xi:include href="xml/ges-transition-clip.xml"/>
<xi:include href="xml/ges-effect-clip.xml"/>
+ <xi:include href="xml/ges-group.xml"/>
</chapter>
<chapter>
diff --git a/docs/libs/ges-sections.txt b/docs/libs/ges-sections.txt
index 4a9eaa1..2475886 100644
--- a/docs/libs/ges-sections.txt
+++ b/docs/libs/ges-sections.txt
@@ -1092,6 +1092,23 @@ GES_URI_CLIP_ASSET_GET_CLASS
</SECTION>
<SECTION>
+<FILE>ges-group</FILE>
+<TITLE>GESGroup</TITLE>
+GESGroup
+ges_group_new
+<SUBSECTION Standard>
+GESGroupClass
+GESGroupPrivate
+GES_GROUP
+GES_IS_GROUP
+GES_TYPE_GROUP
+ges_group_get_type
+GES_GROUP_CLASS
+GES_IS_GROUP_CLASS
+GES_GROUP_GET_CLASS
+</SECTION>
+
+<SECTION>
<FILE>ges-asset-track-file-source</FILE>
<TITLE>GESUriSourceAsset</TITLE>
GESUriSourceAsset
diff --git a/ges/Makefile.am b/ges/Makefile.am
index 9642e3a..eb26aa7 100644
--- a/ges/Makefile.am
+++ b/ges/Makefile.am
@@ -64,6 +64,7 @@ libges_@GST_API_VERSION@_la_SOURCES = \
ges-smart-adder.c \
ges-smart-video-mixer.c \
ges-utils.c \
+ ges-group.c \
gstframepositionner.c
libges_@GST_API_VERSION@includedir = $(includedir)/gstreamer-@GST_API_VERSION@/ges/
@@ -125,6 +126,7 @@ libges_@GST_API_VERSION@include_HEADERS = \
ges-smart-adder.h \
ges-smart-video-mixer.h \
ges-utils.h \
+ ges-group.h \
gstframepositionner.h
noinst_HEADERS = \
diff --git a/ges/ges-clip.c b/ges/ges-clip.c
index 66e764d..3d5ec81 100644
--- a/ges/ges-clip.c
+++ b/ges/ges-clip.c
@@ -686,7 +686,9 @@ ges_clip_class_init (GESClipClass * klass)
/**
* GESClip:layer:
*
- * The GESLayer where this clip is being used.
+ * The GESLayer where this clip is being used. If you want to connect to its
+ * notify signal you should connect to it with g_signal_connect_after as the
+ * signal emission can be stop in the first fase.
*/
properties[PROP_LAYER] = g_param_spec_object ("layer", "Layer",
"The GESLayer where this clip is being used.",
diff --git a/ges/ges-container.c b/ges/ges-container.c
index 4b6c20c..13b178b 100644
--- a/ges/ges-container.c
+++ b/ges/ges-container.c
@@ -325,8 +325,11 @@ _child_start_changed_cb (GESTimelineElement * child,
map = g_hash_table_lookup (priv->mappings, child);
g_assert (map);
- GST_FIXME_OBJECT (container, "We should make sure that our child does not"
- "involve our start becoming < 0. In that case, undo the child move.");
+ if (container->children_control_mode == GES_CHILDREN_UPDATE_OFFSETS) {
+ map->start_offset = _START (container) - _START (child);
+
+ return;
+ }
/* We update all the children calling our set_start method */
container->initiated_move = child;
@@ -349,6 +352,12 @@ _child_inpoint_changed_cb (GESTimelineElement * child,
map = g_hash_table_lookup (priv->mappings, child);
g_assert (map);
+ if (container->children_control_mode == GES_CHILDREN_UPDATE_OFFSETS) {
+ map->inpoint_offset = _START (container) - _START (child);
+
+ return;
+ }
+
/* We update all the children calling our set_inpoint method */
container->initiated_move = child;
_set_inpoint0 (element, _INPOINT (child) + map->inpoint_offset);
@@ -370,6 +379,12 @@ _child_duration_changed_cb (GESTimelineElement * child,
map = g_hash_table_lookup (priv->mappings, child);
g_assert (map);
+ if (container->children_control_mode == GES_CHILDREN_UPDATE_OFFSETS) {
+ map->inpoint_offset = _START (container) - _START (child);
+
+ return;
+ }
+
/* We update all the children calling our set_duration method */
container->initiated_move = child;
_set_duration0 (element, _DURATION (child) + map->duration_offset);
diff --git a/ges/ges-container.h b/ges/ges-container.h
index 3edbd47..26542fa 100644
--- a/ges/ges-container.h
+++ b/ges/ges-container.h
@@ -44,6 +44,7 @@ typedef enum
GES_CHILDREN_UPDATE,
GES_CHILDREN_IGNORE_NOTIFIES,
GES_CHILDREN_UPDATE_OFFSETS,
+ GES_CHILDREN_LAST
} GESChildrenControlMode;
/**
diff --git a/ges/ges-group.c b/ges/ges-group.c
new file mode 100644
index 0000000..777ce03
--- /dev/null
+++ b/ges/ges-group.c
@@ -0,0 +1,550 @@
+/* GStreamer Editing Services
+ * Copyright (C) 2013 Thibault Saunier <thibault.saunier@collabora.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:ges-group
+ * @short_description: Class that permits to group GESClip-s in a timeline,
+ * letting the user manage it a single GESTimelineElement
+ *
+ * A #GESGroup is an object which controls one or more
+ * #GESClips in one or more #GESLayer(s).
+ *
+ * To instanciate a group, you should use the ges_container_group method,
+ * this will be responsible for deciding what subclass of #GESContainer
+ * should be instaciated to group the various #GESTimelineElement passed
+ * in parametter.
+ */
+
+#include "ges-group.h"
+#include "ges.h"
+#include "ges-internal.h"
+
+#include <string.h>
+
+#define parent_class ges_group_parent_class
+G_DEFINE_TYPE (GESGroup, ges_group, GES_TYPE_CONTAINER);
+
+#define GES_CHILDREN_INIBIT_SIGNAL_EMISSION (GES_CHILDREN_LAST + 1)
+
+struct _GESGroupPrivate
+{
+ gboolean reseting_start;
+
+ guint32 max_layer_prio;
+
+ /* This is used while were are setting ourselve a proper timing value,
+ * in this case the value should always be kept */
+ gboolean setting_value;
+};
+
+enum
+{
+ PROP_0,
+ PROP_LAST
+};
+
+/* static GParamSpec *properties[PROP_LAST]; */
+
+/****************************************************
+ * Our listening of children *
+ ****************************************************/
+static void
+_update_our_values (GESGroup * group)
+{
+ GList *tmp;
+ GESContainer *container = GES_CONTAINER (group);
+ guint32 min_layer_prio = G_MAXINT32, max_layer_prio = 0;
+
+ for (tmp = GES_CONTAINER_CHILDREN (group); tmp; tmp = tmp->next) {
+ GESContainer *child = tmp->data;
+
+ if (GES_IS_CLIP (child)) {
+ GESLayer *layer = ges_clip_get_layer (GES_CLIP (child));
+ gint32 prio = ges_layer_get_priority (layer);
+
+ min_layer_prio = MIN (prio, min_layer_prio);
+ max_layer_prio = MAX (prio, max_layer_prio);
+ } else if (GES_IS_GROUP (child)) {
+ gint32 prio = _PRIORITY (child), height = GES_CONTAINER_HEIGHT (child);
+
+ min_layer_prio = MIN (prio, min_layer_prio);
+ max_layer_prio = MAX ((prio + height), max_layer_prio);
+ }
+ }
+
+ if (min_layer_prio != _PRIORITY (group)) {
+ group->priv->setting_value = TRUE;
+ _set_priority0 (GES_TIMELINE_ELEMENT (group), min_layer_prio);
+ group->priv->setting_value = FALSE;
+ for (tmp = GES_CONTAINER_CHILDREN (group); tmp; tmp = tmp->next) {
+ GESTimelineElement *child = tmp->data;
+ guint32 child_prio = GES_IS_CLIP (child) ?
+ ges_clip_get_layer_priority (GES_CLIP (child)) : _PRIORITY (child);
+
+ _ges_container_set_priority_offset (container,
+ child, min_layer_prio - child_prio);
+ }
+ }
+
+ group->priv->max_layer_prio = max_layer_prio;
+ _ges_container_set_height (GES_CONTAINER (group),
+ max_layer_prio - min_layer_prio + 1);
+}
+
+static void
+_child_clip_changed_layer_cb (GESTimelineElement * clip,
+ GParamSpec * arg G_GNUC_UNUSED, GESGroup * group)
+{
+ gint offset, layer_prio = ges_clip_get_layer_priority (GES_CLIP (clip));
+ GESContainer *container = GES_CONTAINER (group);
+
+ if (container->children_control_mode != GES_CHILDREN_UPDATE) {
+ if (container->children_control_mode == GES_CHILDREN_INIBIT_SIGNAL_EMISSION) {
+ container->children_control_mode = GES_CHILDREN_UPDATE;
+ g_signal_stop_emission_by_name (clip, "notify::layer");
+ }
+ return;
+ }
+
+ offset = _ges_container_get_priority_offset (container, clip);
+
+ if (layer_prio + offset < 0 ||
+ (GES_TIMELINE_ELEMENT_TIMELINE (group) &&
+ layer_prio + offset + GES_CONTAINER_HEIGHT (group) - 1 >
+ g_list_length (GES_TIMELINE_ELEMENT_TIMELINE (group)->layers))) {
+ GESLayer *old_layer =
+ g_list_nth_data (GES_TIMELINE_ELEMENT_TIMELINE (group)->layers,
+ _PRIORITY (group) - offset);
+
+ GST_INFO_OBJECT (container, "Trying to move to a layer outside of"
+ "the timeline layers, moving back to old layer (prio %i)",
+ _PRIORITY (group) - offset);
+
+ container->children_control_mode = GES_CHILDREN_INIBIT_SIGNAL_EMISSION;
+ ges_clip_move_to_layer (GES_CLIP (clip), old_layer);
+ g_signal_stop_emission_by_name (clip, "notify::layer");
+
+ return;
+ }
+
+ container->initiated_move = clip;
+ _set_priority0 (GES_TIMELINE_ELEMENT (group), layer_prio + offset);
+ container->initiated_move = NULL;
+}
+
+static void
+_child_group_priority_changed (GESTimelineElement * child,
+ GParamSpec * arg G_GNUC_UNUSED, GESGroup * group)
+{
+ gint offset;
+ GESContainer *container = GES_CONTAINER (group);
+
+ if (container->children_control_mode != GES_CHILDREN_UPDATE) {
+ GST_DEBUG_OBJECT (group, "Ignoring updated");
+ return;
+ }
+
+ offset = _ges_container_get_priority_offset (container, child);
+
+ if (_PRIORITY (group) + offset < 0 ||
+ (GES_TIMELINE_ELEMENT_TIMELINE (group) &&
+ _PRIORITY (group) + offset + GES_CONTAINER_HEIGHT (group) >
+ g_list_length (GES_TIMELINE_ELEMENT_TIMELINE (group)->layers))) {
+
+ GST_WARNING_OBJECT (container, "Trying to move to a layer outside of"
+ "the timeline layers");
+
+ return;
+ }
+
+ container->initiated_move = child;
+ _set_priority0 (GES_TIMELINE_ELEMENT (group), _PRIORITY (child) + offset);
+ container->initiated_move = NULL;
+}
+
+/****************************************************
+ * GESTimelineElement vmethods *
+ ****************************************************/
+static gboolean
+_trim (GESTimelineElement * group, GstClockTime start)
+{
+ GList *tmp;
+ GstClockTime last_child_end = 0;
+ GESContainer *container = GES_CONTAINER (group);
+ GESTimeline *timeline = GES_TIMELINE_ELEMENT_TIMELINE (group);
+ gboolean ret = TRUE, expending = (start < _START (group));
+
+ if (timeline == NULL) {
+ GST_DEBUG ("Not in a timeline yet");
+
+ return FALSE;
+ }
+
+ container->children_control_mode = GES_CHILDREN_IGNORE_NOTIFIES;
+ for (tmp = GES_CONTAINER_CHILDREN (group); tmp; tmp = tmp->next) {
+ GESTimelineElement *child = tmp->data;
+
+ if (expending) {
+ /* If the start if bigger, we do not touch it (in case we are expending)
+ */
+ if (_START (child) > _START (group))
+ continue;
+ ret &= ges_timeline_element_trim (child, start);
+ } else {
+ if (start > _END (child))
+ ret &= ges_timeline_element_trim (child, _END (child));
+ else if (_START (child) < start && _DURATION (child))
+ ret &= ges_timeline_element_trim (child, start);
+
+ }
+ }
+
+ for (tmp = GES_CONTAINER_CHILDREN (group); tmp; tmp = tmp->next) {
+ if (_DURATION (tmp->data))
+ last_child_end =
+ MAX (GES_TIMELINE_ELEMENT_END (tmp->data), last_child_end);
+ }
+
+ GES_GROUP (group)->priv->setting_value = TRUE;
+ _set_start0 (group, start);
+ _set_duration0 (group, last_child_end - start);
+ GES_GROUP (group)->priv->setting_value = FALSE;
+ container->children_control_mode = GES_CHILDREN_UPDATE;
+
+ return ret;
+}
+
+static gboolean
+_set_priority (GESTimelineElement * element, guint32 priority)
+{
+ GList *tmp, *layers;
+ gint diff = priority - _PRIORITY (element);
+ GESContainer *container = GES_CONTAINER (element);
+
+ if (GES_GROUP (element)->priv->setting_value == TRUE)
+ return TRUE;
+
+ container->children_control_mode = GES_CHILDREN_IGNORE_NOTIFIES;
+ layers = GES_TIMELINE_ELEMENT_TIMELINE (element) ?
+ GES_TIMELINE_ELEMENT_TIMELINE (element)->layers : NULL;
+
+ if (layers == NULL) {
+ GST_WARNING_OBJECT (element, "Not any layer in the timeline, not doing"
+ "anything, timeline: %" GST_PTR_FORMAT,
+ GES_TIMELINE_ELEMENT_TIMELINE (element));
+
+ return FALSE;
+ } else if (priority + GES_CONTAINER_HEIGHT (container) - 1 >
+ g_list_length (layers)) {
+ GST_WARNING_OBJECT (container, "Trying to move to a layer outside of"
+ "the timeline layers");
+ return FALSE;
+ }
+
+ for (tmp = GES_CONTAINER_CHILDREN (element); tmp; tmp = tmp->next) {
+ GESTimelineElement *child = tmp->data;
+
+ if (child != container->initiated_move) {
+ if (GES_IS_CLIP (child)) {
+ guint32 layer_prio =
+ ges_clip_get_layer_priority (GES_CLIP (child)) + diff;
+
+ GST_DEBUG_OBJECT (child, "moving from layer: %i to %i",
+ ges_clip_get_layer_priority (GES_CLIP (child)), layer_prio);
+ ges_clip_move_to_layer (GES_CLIP (child),
+ g_list_nth_data (layers, layer_prio));
+ } else if (GES_IS_GROUP (child)) {
+ GST_DEBUG_OBJECT (child, "moving from %i to %i",
+ _PRIORITY (child), diff + _PRIORITY (child));
+ ges_timeline_element_set_priority (child, diff + _PRIORITY (child));
+ }
+ }
+ }
+ container->children_control_mode = GES_CHILDREN_UPDATE;
+
+ return TRUE;
+}
+
+static gboolean
+_set_start (GESTimelineElement * element, GstClockTime start)
+{
+ GList *tmp;
+ gint64 diff = start - _START (element);
+ GESContainer *container = GES_CONTAINER (element);
+
+ if (GES_GROUP (element)->priv->setting_value == TRUE)
+ /* Let GESContainer update itself */
+ return GES_TIMELINE_ELEMENT_CLASS (parent_class)->set_start (element,
+ start);
+
+
+ container->children_control_mode = GES_CHILDREN_IGNORE_NOTIFIES;
+ for (tmp = GES_CONTAINER_CHILDREN (element); tmp; tmp = tmp->next) {
+ GESTimelineElement *child = (GESTimelineElement *) tmp->data;
+
+ if (child != container->initiated_move &&
+ (_END (child) > _START (element) || _END (child) > start)) {
+ _set_start0 (child, _START (child) + diff);
+ }
+ }
+ container->children_control_mode = GES_CHILDREN_UPDATE;
+
+ return TRUE;
+}
+
+static gboolean
+_set_inpoint (GESTimelineElement * element, GstClockTime inpoint)
+{
+ return FALSE;
+}
+
+static gboolean
+_set_duration (GESTimelineElement * element, GstClockTime duration)
+{
+ GList *tmp;
+ GstClockTime last_child_end = 0, new_end;
+ GESContainer *container = GES_CONTAINER (element);
+ GESGroupPrivate *priv = GES_GROUP (element)->priv;
+
+ if (priv->setting_value == TRUE)
+ /* Let GESContainer update itself */
+ return GES_TIMELINE_ELEMENT_CLASS (parent_class)->set_duration (element,
+ duration);
+
+ if (container->initiated_move == NULL) {
+ gboolean expending = (_DURATION (element) < duration);
+
+ new_end = _START (element) + duration;
+ container->children_control_mode = GES_CHILDREN_IGNORE_NOTIFIES;
+ for (tmp = GES_CONTAINER_CHILDREN (element); tmp; tmp = tmp->next) {
+ GESTimelineElement *child = tmp->data;
+ GstClockTime n_dur;
+
+ if ((!expending && _END (child) > new_end) ||
+ (expending && (_END (child) >= _END (element)))) {
+ n_dur = MAX (0, ((gint64) (new_end - _START (child))));
+ _set_duration0 (child, n_dur);
+ }
+ }
+ container->children_control_mode = GES_CHILDREN_UPDATE;
+ }
+
+ for (tmp = GES_CONTAINER_CHILDREN (element); tmp; tmp = tmp->next) {
+ if (_DURATION (tmp->data))
+ last_child_end =
+ MAX (GES_TIMELINE_ELEMENT_END (tmp->data), last_child_end);
+ }
+
+ priv->setting_value = TRUE;
+ _set_duration0 (element, last_child_end - _START (element));
+ priv->setting_value = FALSE;
+
+ return FALSE;
+}
+
+/****************************************************
+ * *
+ * GESContainer virtual methods implementation *
+ * *
+ ****************************************************/
+
+static gboolean
+_add_child (GESContainer * group, GESTimelineElement * child)
+{
+ g_return_val_if_fail (GES_IS_CONTAINER (child), FALSE);
+
+ return TRUE;
+}
+
+static void
+_child_added (GESContainer * group, GESTimelineElement * child)
+{
+ GList *children, *tmp;
+
+ GESGroupPrivate *priv = GES_GROUP (group)->priv;
+ GstClockTime last_child_end = 0, first_child_start = G_MAXUINT64;
+
+ children = GES_CONTAINER_CHILDREN (group);
+
+ for (tmp = children; tmp; tmp = tmp->next) {
+ last_child_end = MAX (GES_TIMELINE_ELEMENT_END (tmp->data), last_child_end);
+ first_child_start =
+ MIN (GES_TIMELINE_ELEMENT_START (tmp->data), first_child_start);
+ }
+
+ priv->setting_value = TRUE;
+ if (first_child_start != GES_TIMELINE_ELEMENT_START (group)) {
+ group->children_control_mode = GES_CHILDREN_IGNORE_NOTIFIES;
+ _set_start0 (GES_TIMELINE_ELEMENT (group), first_child_start);
+ }
+
+ if (last_child_end != GES_TIMELINE_ELEMENT_END (group)) {
+ _set_duration0 (GES_TIMELINE_ELEMENT (group),
+ last_child_end - first_child_start);
+ }
+ priv->setting_value = FALSE;
+
+ group->children_control_mode = GES_CHILDREN_UPDATE;
+ _update_our_values (GES_GROUP (group));
+
+ if (GES_IS_CLIP (child)) {
+ g_signal_connect (child, "notify::layer",
+ (GCallback) _child_clip_changed_layer_cb, group);
+ } else if (GES_IS_GROUP (child), group) {
+ g_signal_connect (child, "notify::priority",
+ (GCallback) _child_group_priority_changed, group);
+ }
+}
+
+static void
+_child_removed (GESContainer * group, GESTimelineElement * child)
+{
+ GList *children;
+ GstClockTime first_child_start;
+
+ _ges_container_sort_children (group);
+
+ children = GES_CONTAINER_CHILDREN (group);
+
+ if (GES_IS_CLIP (child))
+ g_signal_handlers_disconnect_by_func (child, _child_clip_changed_layer_cb,
+ group);
+ else if (GES_IS_GROUP (child), group)
+ g_signal_handlers_disconnect_by_func (child, _child_group_priority_changed,
+ group);
+
+ if (children == NULL) {
+ GST_FIXME_OBJECT (group, "Auto destroy myself?");
+ return;
+ }
+
+ first_child_start = GES_TIMELINE_ELEMENT_START (children->data);
+ if (first_child_start > GES_TIMELINE_ELEMENT_START (group)) {
+ group->children_control_mode = GES_CHILDREN_IGNORE_NOTIFIES;
+ _set_start0 (GES_TIMELINE_ELEMENT (group), first_child_start);
+ group->children_control_mode = GES_CHILDREN_UPDATE;
+ }
+}
+
+static GList *
+_ungroup (GESContainer * group, gboolean recursive)
+{
+ GList *children, *tmp, *ret = NULL;
+
+ children = ges_container_get_children (group);
+ for (tmp = children; tmp; tmp = tmp->next) {
+ GESTimelineElement *child = tmp->data;
+
+ gst_object_ref (child);
+ ges_container_remove (group, child);
+ ret = g_list_append (ret, child);
+ }
+ g_list_free_full (children, gst_object_unref);
+
+ timeline_remove_group (GES_TIMELINE_ELEMENT_TIMELINE (group),
+ GES_GROUP (group));
+
+ return ret;
+}
+
+static GESContainer *
+_group (GList * containers)
+{
+ GList *tmp;
+ GESTimeline *timeline = NULL;
+ GESContainer *ret = g_object_new (GES_TYPE_GROUP, NULL);
+
+ for (tmp = containers; tmp; tmp = tmp->next) {
+ if (!timeline) {
+ timeline = GES_TIMELINE_ELEMENT_TIMELINE (tmp->data);
+ } else if (timeline != GES_TIMELINE_ELEMENT_TIMELINE (tmp->data)) {
+ g_object_unref (ret);
+
+ return NULL;
+ }
+
+ ges_container_add (ret, tmp->data);
+ }
+
+ timeline_add_group (timeline, GES_GROUP (ret));
+
+ return ret;
+}
+
+
+/****************************************************
+ * *
+ * GObject virtual methods implementation *
+ * *
+ ****************************************************/
+static void
+ges_group_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ switch (property_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_group_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ switch (property_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_group_class_init (GESGroupClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GESContainerClass *container_class = GES_CONTAINER_CLASS (klass);
+ GESTimelineElementClass *element_class = GES_TIMELINE_ELEMENT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (GESGroupPrivate));
+
+ object_class->get_property = ges_group_get_property;
+ object_class->set_property = ges_group_set_property;
+
+ element_class->trim = _trim;
+ element_class->set_duration = _set_duration;
+ element_class->set_inpoint = _set_inpoint;
+ element_class->set_start = _set_start;
+ element_class->set_priority = _set_priority;
+ /* TODO implement the deep_copy Virtual method */
+
+ container_class->add_child = _add_child;
+ container_class->child_added = _child_added;
+ container_class->child_removed = _child_removed;
+ container_class->ungroup = _ungroup;
+ container_class->group = _group;
+ container_class->grouping_priority = 0;
+}
+
+static void
+ges_group_init (GESGroup * self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ GES_TYPE_GROUP, GESGroupPrivate);
+
+ self->priv->setting_value = FALSE;
+}
diff --git a/ges/ges-group.h b/ges/ges-group.h
new file mode 100644
index 0000000..826b85e
--- /dev/null
+++ b/ges/ges-group.h
@@ -0,0 +1,56 @@
+/* * Gstreamer
+ *
+ * Copyright (C) <2013> Thibault Saunier <thibault.saunier@collabora.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef GES_GROUP_H
+#define GES_GROUP_H
+
+#include <glib-object.h>
+#include <ges/ges-types.h>
+#include <ges/ges-container.h>
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_GROUP (ges_group_get_type ())
+#define GES_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_GROUP, GESGroup))
+#define GES_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_GROUP, GESGroupClass))
+#define GES_IS_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_GROUP))
+#define GES_IS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_GROUP))
+#define GES_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_GROUP, GESGroupClass))
+
+typedef struct _GESGroupPrivate GESGroupPrivate;
+
+struct _GESGroup {
+ GESContainer parent;
+
+ /*< private >*/
+ GESGroupPrivate *priv;
+
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+struct _GESGroupClass {
+ GESContainerClass parent_class;
+
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GType ges_group_get_type (void);
+
+G_END_DECLS
+#endif /* _GES_GROUP_H */
diff --git a/ges/ges-internal.h b/ges/ges-internal.h
index ccdec6f..e03fd7e 100644
--- a/ges/ges-internal.h
+++ b/ges/ges-internal.h
@@ -84,6 +84,14 @@ G_GNUC_INTERNAL gboolean
timeline_context_to_layer (GESTimeline *timeline, gint offset);
G_GNUC_INTERNAL void
+timeline_add_group (GESTimeline *timeline,
+ GESGroup *group);
+G_GNUC_INTERNAL
+void
+timeline_remove_group (GESTimeline *timeline,
+ GESGroup *group);
+
+G_GNUC_INTERNAL void
ges_asset_cache_init (void);
G_GNUC_INTERNAL void
diff --git a/ges/ges-timeline-element.c b/ges/ges-timeline-element.c
index 5fc88b4..744abf9 100644
--- a/ges/ges-timeline-element.c
+++ b/ges/ges-timeline-element.c
@@ -384,8 +384,8 @@ ges_timeline_element_set_start (GESTimelineElement * self, GstClockTime start)
klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
GST_DEBUG_OBJECT (self, "current start: %" GST_TIME_FORMAT
- " new start: %" GST_TIME_FORMAT, GST_TIME_ARGS (start),
- GST_TIME_ARGS (GES_TIMELINE_ELEMENT_START (self)));
+ " new start: %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (GES_TIMELINE_ELEMENT_START (self)), GST_TIME_ARGS (start));
if (klass->set_start) {
if (klass->set_start (self, start)) {
@@ -393,6 +393,8 @@ ges_timeline_element_set_start (GESTimelineElement * self, GstClockTime start)
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_START]);
}
+ GST_DEBUG_OBJECT (self, "New start: %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (GES_TIMELINE_ELEMENT_START (self)));
return;
}
@@ -489,8 +491,9 @@ ges_timeline_element_set_duration (GESTimelineElement * self,
klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
GST_DEBUG_OBJECT (self, "current duration: %" GST_TIME_FORMAT
- " new duration: %" GST_TIME_FORMAT, GST_TIME_ARGS (duration),
- GST_TIME_ARGS (GES_TIMELINE_ELEMENT_DURATION (self)));
+ " new duration: %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (GES_TIMELINE_ELEMENT_DURATION (self)),
+ GST_TIME_ARGS (duration));
if (klass->set_duration) {
if (klass->set_duration (self, duration)) {
diff --git a/ges/ges-timeline.c b/ges/ges-timeline.c
index 28786e6..d10bdb0 100644
--- a/ges/ges-timeline.c
+++ b/ges/ges-timeline.c
@@ -119,10 +119,10 @@ struct _MoveContext
GList *moving_trackelements;
/* We use it as a set of Clip to move between layers */
- GHashTable *moving_clips;
- /* Min priority of the objects currently in moving_clips */
+ GHashTable *toplevel_containers;
+ /* Min priority of the objects currently in toplevel_containers */
guint min_move_layer;
- /* Max priority of the objects currently in moving_clips */
+ /* Max priority of the objects currently in toplevel_containers */
guint max_layer_prio;
/* Never trim so duration would becomes < 0 */
@@ -184,6 +184,7 @@ struct _GESTimelinePrivate
/* While we are creating and adding the TrackElements for a clip, we need to
* ignore the child-added signal */
GESClip *ignore_track_element_added;
+ GList *groups;
};
/* private structure to contain our track-related information */
@@ -324,9 +325,12 @@ ges_timeline_dispose (GObject * object)
* objects aren't notified that their gnlobjects have been destroyed.
*/
- while (tl->tracks) {
+ while (tl->tracks)
ges_timeline_remove_track (GES_TIMELINE (object), tl->tracks->data);
- }
+
+ while (priv->groups)
+ g_list_free_full (ges_container_ungroup (priv->groups->data, FALSE),
+ gst_object_unref);
g_hash_table_unref (priv->by_start);
g_hash_table_unref (priv->by_end);
@@ -336,7 +340,7 @@ ges_timeline_dispose (GObject * object)
g_sequence_free (priv->starts_ends);
g_sequence_free (priv->tracksources);
g_list_free (priv->movecontext.moving_trackelements);
- g_hash_table_unref (priv->movecontext.moving_clips);
+ g_hash_table_unref (priv->movecontext.toplevel_containers);
g_hash_table_unref (priv->auto_transitions);
@@ -552,6 +556,18 @@ ges_timeline_init (GESTimeline * self)
/* Private methods */
+static inline GESContainer *
+get_toplevel_container (GESTrackElement * element)
+{
+ GESTimelineElement *ret =
+ ges_timeline_element_get_toplevel_parent ((GESTimelineElement
+ *) (element));
+
+ /* We own a ref to the elements ourself */
+ gst_object_unref (ret);
+ return (GESContainer *) ret;
+}
+
/* Sorting utils*/
static gint
sort_layers (gpointer a, gpointer b)
@@ -885,7 +901,8 @@ static inline void
init_movecontext (MoveContext * mv_ctx, gboolean first_init)
{
if (G_UNLIKELY (first_init))
- mv_ctx->moving_clips = g_hash_table_new (g_direct_hash, g_direct_equal);
+ mv_ctx->toplevel_containers =
+ g_hash_table_new (g_direct_hash, g_direct_equal);
mv_ctx->moving_trackelements = NULL;
mv_ctx->max_trim_pos = G_MAXUINT64;
@@ -900,7 +917,7 @@ static inline void
clean_movecontext (MoveContext * mv_ctx)
{
g_list_free (mv_ctx->moving_trackelements);
- g_hash_table_remove_all (mv_ctx->moving_clips);
+ g_hash_table_remove_all (mv_ctx->toplevel_containers);
init_movecontext (mv_ctx, FALSE);
}
@@ -1060,7 +1077,7 @@ ges_timeline_snap_position (GESTimeline * timeline,
GESTimelinePrivate *priv = timeline->priv;
GSequenceIter *iter, *prev_iter, *nxt_iter;
GESTrackElement *tmp_trackelement;
- GESClip *tmp_clip, *clip;
+ GESContainer *tmp_container, *container;
GstClockTime *last_snap_ts = priv->movecontext.last_snap_ts;
guint64 snap_distance = timeline->priv->snapping_distance;
@@ -1081,7 +1098,7 @@ ges_timeline_snap_position (GESTimeline * timeline,
}
}
- clip = GES_CLIP (GES_TIMELINE_ELEMENT_PARENT (trackelement));
+ container = get_toplevel_container (trackelement);
iter = g_sequence_search (priv->starts_ends, &timecode,
(GCompareDataFunc) compare_uint64, NULL);
@@ -1092,10 +1109,11 @@ ges_timeline_snap_position (GESTimeline * timeline,
while (!g_sequence_iter_is_end (nxt_iter)) {
next_tc = g_sequence_get (iter);
tmp_trackelement = g_hash_table_lookup (timeline->priv->by_object, next_tc);
- tmp_clip = GES_CLIP (GES_TIMELINE_ELEMENT_PARENT (tmp_trackelement));
+ tmp_container = get_toplevel_container (tmp_trackelement);
off = timecode > *next_tc ? timecode - *next_tc : *next_tc - timecode;
- if (next_tc != current && off <= snap_distance && clip != tmp_clip) {
+ if (next_tc != current && off <= snap_distance
+ && container != tmp_container) {
ret = next_tc;
break;
@@ -1111,11 +1129,11 @@ ges_timeline_snap_position (GESTimeline * timeline,
while (!g_sequence_iter_is_begin (prev_iter)) {
prev_tc = g_sequence_get (prev_iter);
tmp_trackelement = g_hash_table_lookup (timeline->priv->by_object, prev_tc);
- tmp_clip = GES_CLIP (GES_TIMELINE_ELEMENT_PARENT (tmp_trackelement));
+ tmp_container = get_toplevel_container (tmp_trackelement);
off1 = timecode > *prev_tc ? timecode - *prev_tc : *prev_tc - timecode;
if (prev_tc != current && off1 < off && off1 <= snap_distance &&
- clip != tmp_clip) {
+ container != tmp_container) {
ret = prev_tc;
break;
@@ -1139,35 +1157,39 @@ done:
return ret;
}
-static inline GESClip *
-add_moving_clip (MoveContext * mv_ctx, GESTrackElement * trackelement)
+static inline GESContainer *
+add_toplevel_container (MoveContext * mv_ctx, GESTrackElement * trackelement)
{
- GESClip *clip;
- GESLayer *layer;
guint layer_prio;
-
- clip = GES_CLIP (GES_TIMELINE_ELEMENT_PARENT (trackelement));
+ GESContainer *toplevel = get_toplevel_container (trackelement);
/* Avoid recalculating */
- if (!g_hash_table_lookup (mv_ctx->moving_clips, clip)) {
- layer = ges_clip_get_layer (clip);
- if (layer == NULL) {
- GST_WARNING_OBJECT (clip, "Not in any layer, can not move"
- " between layers");
+ if (!g_hash_table_lookup (mv_ctx->toplevel_containers, toplevel)) {
+ if (GES_IS_CLIP (toplevel)) {
- } else {
+ layer_prio = ges_clip_get_layer_priority (GES_CLIP (toplevel));
+ if (layer_prio == (guint32) - 1) {
+ GST_WARNING_OBJECT (toplevel, "Not in any layer, can not move"
+ " between layers");
- g_hash_table_insert (mv_ctx->moving_clips, clip, clip);
-
- layer_prio = ges_layer_get_priority (layer);
+ return toplevel;
+ }
mv_ctx->min_move_layer = MIN (mv_ctx->min_move_layer, layer_prio);
mv_ctx->max_layer_prio = MAX (mv_ctx->max_layer_prio, layer_prio);
+ } else if GES_IS_GROUP
+ (toplevel) {
+ mv_ctx->min_move_layer = MIN (mv_ctx->min_move_layer,
+ _PRIORITY (toplevel));
+ mv_ctx->max_layer_prio = MAX (mv_ctx->max_layer_prio,
+ _PRIORITY (toplevel) + GES_CONTAINER_HEIGHT (toplevel));
+ } else
+ g_assert_not_reached ();
+
+ g_hash_table_insert (mv_ctx->toplevel_containers, toplevel, toplevel);
- gst_object_unref (layer);
- }
}
- return clip;
+ return toplevel;
}
static gboolean
@@ -1292,10 +1314,10 @@ ges_timeline_set_moving_context (GESTimeline * timeline, GESTrackElement * obj,
default:
break;
}
- add_moving_clip (&timeline->priv->movecontext, editor_trackelement);
+ add_toplevel_container (&timeline->priv->movecontext, editor_trackelement);
} else {
- /* We add the main object to the moving_clips set */
- add_moving_clip (&timeline->priv->movecontext, obj);
+ /* We add the main object to the toplevel_containers set */
+ add_toplevel_container (&timeline->priv->movecontext, obj);
}
@@ -1387,7 +1409,7 @@ timeline_ripple_object (GESTimeline * timeline, GESTrackElement * obj,
{
GList *tmp, *moved_clips = NULL;
GESTrackElement *trackelement;
- GESClip *clip;
+ GESContainer *container;
guint64 duration, new_start, *snapped, *cur;
gint64 offset;
@@ -1415,11 +1437,11 @@ timeline_ripple_object (GESTimeline * timeline, GESTrackElement * obj,
trackelement = GES_TRACK_ELEMENT (tmp->data);
new_start = _START (trackelement) + offset;
- clip = add_moving_clip (mv_ctx, trackelement);
+ container = add_toplevel_container (mv_ctx, trackelement);
/* Make sure not to move 2 times the same Clip */
- if (g_list_find (moved_clips, clip) == NULL) {
+ if (g_list_find (moved_clips, container) == NULL) {
_set_start0 (GES_TIMELINE_ELEMENT (trackelement), new_start);
- moved_clips = g_list_prepend (moved_clips, clip);
+ moved_clips = g_list_prepend (moved_clips, container);
}
}
@@ -1445,12 +1467,16 @@ timeline_ripple_object (GESTimeline * timeline, GESTrackElement * obj,
trackelement = GES_TRACK_ELEMENT (tmp->data);
new_start = _START (trackelement) + offset;
- clip = add_moving_clip (mv_ctx, trackelement);
+ container = add_toplevel_container (mv_ctx, trackelement);
+ if (GES_IS_GROUP (container))
+ container->children_control_mode = GES_CHILDREN_UPDATE_OFFSETS;
/* Make sure not to move 2 times the same Clip */
- if (g_list_find (moved_clips, clip) == NULL) {
+ if (g_list_find (moved_clips, container) == NULL) {
_set_start0 (GES_TIMELINE_ELEMENT (trackelement), new_start);
- moved_clips = g_list_prepend (moved_clips, clip);
+ moved_clips = g_list_prepend (moved_clips, container);
}
+ if (GES_IS_GROUP (container))
+ container->children_control_mode = GES_CHILDREN_UPDATE;
}
@@ -1629,7 +1655,7 @@ gboolean
timeline_move_object (GESTimeline * timeline, GESTrackElement * object,
GList * layers, GESEdge edge, guint64 position)
{
- if (!ges_timeline_set_moving_context (timeline, object, GES_EDIT_MODE_RIPPLE,
+ if (!ges_timeline_set_moving_context (timeline, object, GES_EDIT_MODE_NORMAL,
edge, layers)) {
GST_DEBUG_OBJECT (object, "Could not move to %" GST_TIME_FORMAT,
GST_TIME_ARGS (position));
@@ -1656,11 +1682,12 @@ ges_timeline_move_object_simple (GESTimeline * timeline,
return FALSE;
track_element = GES_TRACK_ELEMENT (element);
- end = position + _DURATION (track_element);
+ end = position + _DURATION (get_toplevel_container (track_element));
cur = g_hash_table_lookup (timeline->priv->by_end, track_element);
- GST_DEBUG_OBJECT (timeline, "Moving to %" GST_TIME_FORMAT " (end %"
- GST_TIME_FORMAT ")", GST_TIME_ARGS (position), GST_TIME_ARGS (end));
+ GST_DEBUG_OBJECT (timeline, "Moving %" GST_PTR_FORMAT "to %"
+ GST_TIME_FORMAT " (end %" GST_TIME_FORMAT ")", element,
+ GST_TIME_ARGS (position), GST_TIME_ARGS (end));
snap_end = ges_timeline_snap_position (timeline, track_element, cur, end,
FALSE);
@@ -1670,7 +1697,8 @@ ges_timeline_move_object_simple (GESTimeline * timeline,
off1 = G_MAXUINT64;
cur = g_hash_table_lookup (timeline->priv->by_start, track_element);
- snap_st = ges_timeline_snap_position (timeline, track_element, cur, position,
+ snap_st =
+ ges_timeline_snap_position (timeline, track_element, cur, position,
FALSE);
if (snap_st)
off2 = position > *snap_st ? position - *snap_st : *snap_st - position;
@@ -1704,33 +1732,37 @@ timeline_context_to_layer (GESTimeline * timeline, gint offset)
/* Layer's priority is always positive */
if (offset != 0 && (offset > 0 || mv_ctx->min_move_layer >= -offset)) {
GHashTableIter iter;
- GESClip *key, *value;
- GESLayer *new_layer, *layer;
+ GESContainer *key, *value;
+ GESLayer *new_layer;
guint prio;
mv_ctx->ignore_needs_ctx = TRUE;
GST_DEBUG ("Moving %d object, offset %d",
- g_hash_table_size (mv_ctx->moving_clips), offset);
+ g_hash_table_size (mv_ctx->toplevel_containers), offset);
- g_hash_table_iter_init (&iter, mv_ctx->moving_clips);
+ g_hash_table_iter_init (&iter, mv_ctx->toplevel_containers);
while (g_hash_table_iter_next (&iter, (gpointer *) & key,
(gpointer *) & value)) {
- layer = ges_clip_get_layer (value);
- prio = ges_layer_get_priority (layer);
- /* We know that the layer exists as we created it */
- new_layer = GES_LAYER (g_list_nth_data (timeline->layers, prio + offset));
+ if (GES_IS_CLIP (value)) {
+ prio = ges_clip_get_layer_priority (GES_CLIP (value));
- if (new_layer == NULL) {
- do {
- new_layer = ges_timeline_append_layer (timeline);
- } while (ges_layer_get_priority (new_layer) < prio + offset);
- }
+ /* We know that the layer exists as we created it */
+ new_layer =
+ GES_LAYER (g_list_nth_data (timeline->layers, prio + offset));
- ret &= ges_clip_move_to_layer (key, new_layer);
+ if (new_layer == NULL) {
+ do {
+ new_layer = ges_timeline_append_layer (timeline);
+ } while (ges_layer_get_priority (new_layer) < prio + offset);
+ }
- gst_object_unref (layer);
+ ret &= ges_clip_move_to_layer (GES_CLIP (key), new_layer);
+ } else if (GES_IS_GROUP (value)) {
+ _set_priority0 (GES_TIMELINE_ELEMENT (value),
+ _PRIORITY (value) + offset);
+ }
}
/* Readjust min_move_layer */
@@ -1742,6 +1774,30 @@ timeline_context_to_layer (GESTimeline * timeline, gint offset)
return ret;
}
+void
+timeline_add_group (GESTimeline * timeline, GESGroup * group)
+{
+ GST_DEBUG_OBJECT (timeline, "Adding group %" GST_PTR_FORMAT, group);
+
+ timeline->priv->movecontext.needs_move_ctx = TRUE;
+ timeline->priv->groups = g_list_prepend (timeline->priv->groups,
+ gst_object_ref_sink (group));
+
+ ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (group), timeline);
+}
+
+void
+timeline_remove_group (GESTimeline * timeline, GESGroup * group)
+{
+ GST_DEBUG_OBJECT (timeline, "Removing group %" GST_PTR_FORMAT, group);
+
+ timeline->priv->groups = g_list_remove (timeline->priv->groups, group);
+
+ timeline->priv->movecontext.needs_move_ctx = TRUE;
+ ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (group), NULL);
+ gst_object_unref (group);
+}
+
static GPtrArray *
select_tracks_for_object_default (GESTimeline * timeline,
GESClip * clip, GESTrackElement * tr_object, gpointer user_data)
@@ -1800,8 +1856,8 @@ layer_auto_transition_changed_cb (GESLayer * layer,
}
static void
-clip_track_element_added_cb (GESClip * clip, GESTrackElement * track_element,
- GESTimeline * timeline)
+clip_track_element_added_cb (GESClip * clip,
+ GESTrackElement * track_element, GESTimeline * timeline)
{
guint i;
GESTrack *track;
@@ -1885,8 +1941,8 @@ clip_track_element_added_cb (GESClip * clip, GESTrackElement * track_element,
}
static void
-clip_track_element_removed_cb (GESClip * clip, GESTrackElement * track_element,
- GESTimeline * timeline)
+clip_track_element_removed_cb (GESClip * clip,
+ GESTrackElement * track_element, GESTimeline * timeline)
{
GESTrack *track = ges_track_element_get_track (track_element);
@@ -2081,17 +2137,19 @@ track_element_added_cb (GESTrack * track, GESTrackElement * track_element,
/* Auto transition should be updated before we receive the signal */
g_signal_connect_after (GES_TRACK_ELEMENT (track_element), "notify::start",
G_CALLBACK (trackelement_start_changed_cb), timeline);
- g_signal_connect_after (GES_TRACK_ELEMENT (track_element), "notify::duration",
- G_CALLBACK (trackelement_duration_changed_cb), timeline);
- g_signal_connect_after (GES_TRACK_ELEMENT (track_element), "notify::priority",
- G_CALLBACK (trackelement_priority_changed_cb), timeline);
+ g_signal_connect_after (GES_TRACK_ELEMENT (track_element),
+ "notify::duration", G_CALLBACK (trackelement_duration_changed_cb),
+ timeline);
+ g_signal_connect_after (GES_TRACK_ELEMENT (track_element),
+ "notify::priority", G_CALLBACK (trackelement_priority_changed_cb),
+ timeline);
start_tracking_track_element (timeline, track_element);
}
static void
-track_element_removed_cb (GESTrack * track, GESTrackElement * track_element,
- GESTimeline * timeline)
+track_element_removed_cb (GESTrack * track,
+ GESTrackElement * track_element, GESTimeline * timeline)
{
if (GES_IS_SOURCE (track_element)) {
diff --git a/ges/ges-types.h b/ges/ges-types.h
index b7b6262..1516e0e 100644
--- a/ges/ges-types.h
+++ b/ges/ges-types.h
@@ -86,6 +86,9 @@ typedef struct _GESTextOverlayClipClass GESTextOverlayClipClass;
typedef struct _GESEffectClip GESEffectClip;
typedef struct _GESEffectClipClass GESEffectClipClass;
+typedef struct _GESGroup GESGroup;
+typedef struct _GESGroupClass GESGroupClass;
+
typedef struct _GESTrack GESTrack;
typedef struct _GESTrackClass GESTrackClass;
diff --git a/ges/ges.c b/ges/ges.c
index 2b4b54c..d55d6b2 100644
--- a/ges/ges.c
+++ b/ges/ges.c
@@ -78,6 +78,8 @@ ges_init (void)
GES_TYPE_TRANSITION_CLIP;
GES_TYPE_OVERLAY_CLIP;
+ GES_TYPE_GROUP;
+
/* register formatter types with the system */
GES_TYPE_PITIVI_FORMATTER;
GES_TYPE_XML_FORMATTER;
diff --git a/ges/ges.h b/ges/ges.h
index 013527f..202f810 100644
--- a/ges/ges.h
+++ b/ges/ges.h
@@ -45,6 +45,7 @@
#include <ges/ges-custom-source-clip.h>
#include <ges/ges-base-effect-clip.h>
#include <ges/ges-uri-clip.h>
+#include <ges/ges-group.h>
#include <ges/ges-screenshot.h>
#include <ges/ges-asset.h>
#include <ges/ges-clip-asset.h>
diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am
index fab4ae5..b8d0747 100644
--- a/tests/check/Makefile.am
+++ b/tests/check/Makefile.am
@@ -41,6 +41,7 @@ check_PROGRAMS = \
ges/overlays\
ges/text_properties\
ges/mixers\
+ ges/group\
ges/project
noinst_LTLIBRARIES=$(testutils_noisnt_libraries)
diff --git a/tests/check/ges/basic.c b/tests/check/ges/basic.c
index 05e60f4..7faabb9 100644
--- a/tests/check/ges/basic.c
+++ b/tests/check/ges/basic.c
@@ -420,6 +420,7 @@ GST_START_TEST (test_ges_timeline_remove_track)
tmp_layer = ges_clip_get_layer (GES_CLIP (s1));
fail_unless (tmp_layer == layer);
gst_object_unref (tmp_layer);
+ ASSERT_OBJECT_REFCOUNT (layer, "1 for the timeline", 1);
GST_DEBUG ("Creating a source");
s2 = ges_custom_source_clip_new (my_fill_track_func, NULL);
@@ -428,6 +429,7 @@ GST_START_TEST (test_ges_timeline_remove_track)
tmp_layer = ges_clip_get_layer (GES_CLIP (s2));
fail_unless (tmp_layer == layer);
gst_object_unref (tmp_layer);
+ ASSERT_OBJECT_REFCOUNT (layer, "1 for the timeline", 1);
GST_DEBUG ("Creating a source");
s3 = ges_custom_source_clip_new (my_fill_track_func, NULL);
@@ -436,6 +438,7 @@ GST_START_TEST (test_ges_timeline_remove_track)
tmp_layer = ges_clip_get_layer (GES_CLIP (s3));
fail_unless (tmp_layer == layer);
gst_object_unref (tmp_layer);
+ ASSERT_OBJECT_REFCOUNT (layer, "1 for the timeline", 1);
GST_DEBUG ("Add the layer to the timeline");
fail_unless (ges_timeline_add_layer (timeline, layer));
@@ -447,6 +450,7 @@ GST_START_TEST (test_ges_timeline_remove_track)
fail_unless (g_list_find (layers, layer) != NULL);
g_list_foreach (layers, (GFunc) gst_object_unref, NULL);
g_list_free (layers);
+ ASSERT_OBJECT_REFCOUNT (layer, "1 for the timeline", 1);
GST_DEBUG ("Add the track to the timeline");
fail_unless (ges_timeline_add_track (timeline, track));
diff --git a/tests/check/ges/clip.c b/tests/check/ges/clip.c
index ee366a5..0fbb0b5 100644
--- a/tests/check/ges/clip.c
+++ b/tests/check/ges/clip.c
@@ -287,7 +287,11 @@ GST_START_TEST (test_clip_group_ungroup)
assert_equals_uint64 (_DURATION (clip2), 10);
regrouped_clip = ges_container_group (containers);
- fail_unless (regrouped_clip == NULL);
+ fail_unless (GES_IS_GROUP (regrouped_clip));
+ assert_equals_int (g_list_length (GES_CONTAINER_CHILDREN (regrouped_clip)),
+ 2);
+ tmp = ges_container_ungroup (regrouped_clip, FALSE);
+ g_list_free_full (tmp, gst_object_unref);
ges_timeline_element_set_start (GES_TIMELINE_ELEMENT (clip), 0);
regrouped_clip = ges_container_group (containers);
diff --git a/tests/check/ges/group.c b/tests/check/ges/group.c
new file mode 100644
index 0000000..5644b23
--- /dev/null
+++ b/tests/check/ges/group.c
@@ -0,0 +1,500 @@
+/* GStreamer Editing Services
+ *
+ * Copyright (C) <2013> Thibault Saunier <thibault.saunier@collabora.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "test-utils.h"
+#include <ges/ges.h>
+#include <gst/check/gstcheck.h>
+
+GST_START_TEST (test_move_group)
+{
+ GESAsset *asset;
+ GESGroup *group;
+ GESTimeline *timeline;
+ GESLayer *layer, *layer1;
+ GESClip *clip, *clip1, *clip2;
+
+ GList *clips = NULL;
+
+ ges_init ();
+
+ timeline = ges_timeline_new_audio_video ();
+
+ layer = ges_timeline_append_layer (timeline);
+ layer1 = ges_timeline_append_layer (timeline);
+ asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);
+
+ /**
+ * Our timeline:
+ * -------------
+ *
+ * 0------------Group1---------------110
+ * |-------- |
+ * layer: | clip | |
+ * |-------10 |
+ * |----------------------------------|
+ * | 0--------- 0-----------|
+ * layer1: | | clip1 | | clip2 |
+ * | 10--------20 50----------|
+ * |----------------------------------|
+ */
+ clip = ges_layer_add_asset (layer, asset, 0, 0, 10, GES_TRACK_TYPE_UNKNOWN);
+ clip1 =
+ ges_layer_add_asset (layer1, asset, 10, 0, 10, GES_TRACK_TYPE_UNKNOWN);
+ clip2 =
+ ges_layer_add_asset (layer1, asset, 50, 0, 60, GES_TRACK_TYPE_UNKNOWN);
+ clips = g_list_prepend (clips, clip);
+ clips = g_list_prepend (clips, clip1);
+ clips = g_list_prepend (clips, clip2);
+ group = GES_GROUP (ges_container_group (clips));
+ g_list_free (clips);
+
+ fail_unless (GES_IS_GROUP (group));
+ ASSERT_OBJECT_REFCOUNT (group, "1 ref for the timeline", 1);
+ fail_unless (g_list_length (GES_CONTAINER_CHILDREN (group)) == 3);
+ assert_equals_int (GES_CONTAINER_HEIGHT (group), 2);
+ CHECK_OBJECT_PROPS (clip, 0, 0, 10);
+ CHECK_OBJECT_PROPS (clip1, 10, 0, 10);
+ CHECK_OBJECT_PROPS (clip2, 50, 0, 60);
+ CHECK_OBJECT_PROPS (group, 0, 0, 110);
+
+ /*
+ * 0 10------------Group1---------------120
+ * |-------- |
+ * layer: | clip | |
+ * |-------20 |
+ * |----------------------------------|
+ * | 0--------- 0-----------|
+ * layer1: | | clip1 | | clip2 |
+ * | 20--------30 60----------|
+ * |----------------------------------|
+ */
+ ges_timeline_element_set_start (GES_TIMELINE_ELEMENT (clip), 10);
+ CHECK_OBJECT_PROPS (clip, 10, 0, 10);
+ CHECK_OBJECT_PROPS (clip1, 20, 0, 10);
+ CHECK_OBJECT_PROPS (clip2, 60, 0, 60);
+ CHECK_OBJECT_PROPS (group, 10, 0, 110);
+
+ /*
+ * 0 10------------Group1---------------120
+ * |------ |
+ * layer: |clip | |
+ * |-----15 |
+ * |----------------------------------|
+ * | 0--------- 0-----------|
+ * layer1: | | clip1 | | clip2 |
+ * | 20--------30 60----------|
+ * |----------------------------------|
+ */
+ ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (clip), 5);
+ CHECK_OBJECT_PROPS (clip, 10, 0, 5);
+ CHECK_OBJECT_PROPS (clip1, 20, 0, 10);
+ CHECK_OBJECT_PROPS (clip2, 60, 0, 60);
+ CHECK_OBJECT_PROPS (group, 10, 0, 110);
+
+ /*
+ * 0 10------------Group1---------------110
+ * |------ |
+ * layer: |clip | |
+ * |-----15 |
+ * |----------------------------------|
+ * | 0--------- 0-----------|
+ * layer1: | | clip1 | | clip2 |
+ * | 20--------30 60----------|
+ * |----------------------------------|
+ */
+ ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (clip2), 50);
+ CHECK_OBJECT_PROPS (clip, 10, 0, 5);
+ CHECK_OBJECT_PROPS (clip1, 20, 0, 10);
+ CHECK_OBJECT_PROPS (clip2, 60, 0, 50);
+ CHECK_OBJECT_PROPS (group, 10, 0, 100);
+
+ /*
+ * 0 10------------Group1---------------110
+ * |------ |
+ * layer: |clip | |
+ * |-----15 |
+ * |----------------------------------|
+ * | 5--------- 0-----------|
+ * layer1: | | clip1 | | clip2 |
+ * | 20--------30 60----------|
+ * |----------------------------------|
+ */
+ ges_timeline_element_set_inpoint (GES_TIMELINE_ELEMENT (clip1), 5);
+ CHECK_OBJECT_PROPS (clip, 10, 0, 5);
+ CHECK_OBJECT_PROPS (clip1, 20, 5, 10);
+ CHECK_OBJECT_PROPS (clip2, 60, 0, 50);
+ CHECK_OBJECT_PROPS (group, 10, 0, 100);
+
+ /*
+ * 0 10------------Group1---------------110
+ * |------ |
+ * layer: |clip | |
+ * |-----15 |
+ * |----------------------------------|
+ * | 5--------- 0-----------|
+ * layer1: | | clip1 | | clip2 |
+ * | 20--------30 60----------|
+ * |----------------------------------|
+ */
+ ges_timeline_element_set_inpoint (GES_TIMELINE_ELEMENT (clip1), 5);
+ CHECK_OBJECT_PROPS (clip, 10, 0, 5);
+ CHECK_OBJECT_PROPS (clip1, 20, 5, 10);
+ CHECK_OBJECT_PROPS (clip2, 60, 0, 50);
+ CHECK_OBJECT_PROPS (group, 10, 0, 100);
+
+ /*
+ * 0 20---Group1---------------110
+ * | |
+ * layer: | |
+ * | |
+ * |--------------------------|
+ * 5--------- 0------------|
+ * layer1: | clip1 | | clip2 |
+ * 20--------30 60----------|
+ * |--------------------------|
+ */
+ ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 20);
+ CHECK_OBJECT_PROPS (clip, 15, 5, 0);
+ CHECK_OBJECT_PROPS (clip1, 20, 5, 10);
+ CHECK_OBJECT_PROPS (clip2, 60, 0, 50);
+ CHECK_OBJECT_PROPS (group, 20, 0, 90);
+
+ /*
+ * 0 25---Group1---------------110
+ * | |
+ * layer: | |
+ * | |
+ * |--------------------------|
+ * 10------ 0------------|
+ * layer1: | clip1 | | clip2 |
+ * 25------30 60----------|
+ * |--------------------------|
+ */
+ ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 25);
+ CHECK_OBJECT_PROPS (clip, 15, 5, 0);
+ CHECK_OBJECT_PROPS (clip1, 25, 10, 5);
+ CHECK_OBJECT_PROPS (clip2, 60, 0, 50);
+ CHECK_OBJECT_PROPS (group, 25, 0, 85);
+
+ /*
+ * 0 10------------Group1---------------110
+ * |------ |
+ * layer: |clip | |
+ * |-----15 |
+ * |----------------------------------|
+ * 0----------------- 0-----------|
+ * layer1: | clip1 | | clip2 |
+ * |-----------------30 60----------|
+ * |----------------------------------|
+ */
+ ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 10);
+ CHECK_OBJECT_PROPS (clip, 10, 0, 5);
+ CHECK_OBJECT_PROPS (clip1, 10, 0, 20);
+ CHECK_OBJECT_PROPS (clip2, 60, 0, 50);
+ CHECK_OBJECT_PROPS (group, 10, 0, 100);
+
+ /*
+ * 0 25---Group1---------------110
+ * | |
+ * layer: 15 | |
+ * |clip | |
+ * - |--------------------------|
+ * 15------ 0------------|
+ * layer1: | clip1 | | clip2 |
+ * 25------30 60----------|
+ * |--------------------------|
+ */
+ ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 25);
+ CHECK_OBJECT_PROPS (clip, 15, 5, 0);
+ CHECK_OBJECT_PROPS (clip1, 25, 15, 5);
+ CHECK_OBJECT_PROPS (clip2, 60, 0, 50);
+ CHECK_OBJECT_PROPS (group, 25, 0, 85);
+
+ /*
+ * 0 25---Group1--30
+ * | |
+ * layer: 15 | |
+ * |clip | |
+ * - |------------
+ * 15-----------| 60
+ * layer1: | clip1 | |clip2
+ * 25------------| -
+ * |------------|
+ */
+ ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (group), 10);
+ CHECK_OBJECT_PROPS (clip, 15, 5, 0);
+ CHECK_OBJECT_PROPS (clip1, 25, 15, 5);
+ CHECK_OBJECT_PROPS (clip2, 60, 0, 0);
+ CHECK_OBJECT_PROPS (group, 25, 0, 5);
+
+ /*
+ * 0 25---Group1---------------125
+ * | |
+ * layer: 15 | |
+ * |clip | |
+ * - |--------------------------|
+ * 15-------------------------|
+ * layer1: | clip1 | clip2 |
+ * 25--------------60----------|
+ * |--------------------------|
+ */
+ ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (group), 100);
+ CHECK_OBJECT_PROPS (clip, 15, 5, 0);
+ CHECK_OBJECT_PROPS (clip1, 25, 15, 100);
+ CHECK_OBJECT_PROPS (clip2, 60, 0, 65);
+ CHECK_OBJECT_PROPS (group, 25, 0, 100);
+
+ /*
+ * 0 20---Group1---------------120
+ * | |
+ * layer: 15 | |
+ * |clip| |
+ * - |--------------------------|
+ * 15-------------------------|
+ * layer1: | clip1 | clip2 |
+ * 20-------------55----------|
+ * |--------------------------|
+ */
+ ges_timeline_element_set_start (GES_TIMELINE_ELEMENT (group), 20);
+ CHECK_OBJECT_PROPS (clip, 15, 5, 0);
+ CHECK_OBJECT_PROPS (clip1, 20, 15, 100);
+ CHECK_OBJECT_PROPS (clip2, 55, 0, 65);
+ CHECK_OBJECT_PROPS (group, 20, 0, 100);
+
+ /*
+ * 0 10---Group1---------------120
+ * |-----15 |
+ * layer: | clip| |
+ * |------ |
+ * |--------------------------|
+ * 5--------------------------|
+ * layer1: | clip1 | clip2 |
+ * 10-------------55----------|
+ * |--------------------------|
+ */
+ ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 10);
+ CHECK_OBJECT_PROPS (clip, 10, 0, 5);
+ CHECK_OBJECT_PROPS (clip1, 10, 5, 110);
+ CHECK_OBJECT_PROPS (clip2, 55, 0, 65);
+ CHECK_OBJECT_PROPS (group, 10, 0, 110);
+
+ ASSERT_OBJECT_REFCOUNT (group, "1 ref for the timeline", 1);
+ check_destroyed (G_OBJECT (timeline), G_OBJECT (group), NULL);
+ gst_object_unref (asset);
+}
+
+GST_END_TEST;
+
+
+
+static void
+_changed_layer_cb (GESTimelineElement * clip,
+ GParamSpec * arg G_GNUC_UNUSED, guint * nb_calls)
+{
+ *nb_calls = *nb_calls + 1;
+}
+
+GST_START_TEST (test_group_in_group)
+{
+ GESAsset *asset;
+ GESTimeline *timeline;
+ GESGroup *group, *group1;
+ GESLayer *layer, *layer1, *layer2, *layer3;
+ GESClip *c, *c1, *c2, *c3, *c4, *c5;
+
+ guint nb_layer_notifies = 0;
+ GList *clips = NULL;
+
+ ges_init ();
+
+ timeline = ges_timeline_new_audio_video ();
+
+ /* Our timeline
+ *
+ * --0------------10-Group-----20---------------30-------Group1----------70
+ * | +-----------+ |+-----------50 |
+ * L | | C | || C3 | |
+ * | +-----------+ |+-----------+ |
+ * --|-------------------------------------------|-----40----------------|
+ * | +------------+ +-------------+ | +--------60 |
+ * L1 | | C1 | | C2 | | | C4 | |
+ * | +------------+ +-------------+ | +--------+ |
+ * --|-------------------------------------------|-----------------------|
+ * | | +--------+|
+ * L2 | | | c5 ||
+ * | | +--------+|
+ * --+-------------------------------------------+-----------------------+
+ *
+ * L3
+ *
+ * -----------------------------------------------------------------------
+ */
+
+ layer = ges_timeline_append_layer (timeline);
+ layer1 = ges_timeline_append_layer (timeline);
+ layer2 = ges_timeline_append_layer (timeline);
+ layer3 = ges_timeline_append_layer (timeline);
+ assert_equals_int (ges_layer_get_priority (layer3), 3);
+ asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);
+
+ c = ges_layer_add_asset (layer, asset, 0, 0, 10, GES_TRACK_TYPE_UNKNOWN);
+ c1 = ges_layer_add_asset (layer1, asset, 10, 0, 10, GES_TRACK_TYPE_UNKNOWN);
+ c2 = ges_layer_add_asset (layer1, asset, 20, 0, 10, GES_TRACK_TYPE_UNKNOWN);
+ clips = g_list_prepend (clips, c);
+ clips = g_list_prepend (clips, c1);
+ clips = g_list_prepend (clips, c2);
+ group = GES_GROUP (ges_container_group (clips));
+ fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group) == timeline);
+ g_list_free (clips);
+
+ fail_unless (GES_IS_GROUP (group));
+ CHECK_OBJECT_PROPS (c, 0, 0, 10);
+ CHECK_OBJECT_PROPS (c1, 10, 0, 10);
+ CHECK_OBJECT_PROPS (c2, 20, 0, 10);
+ CHECK_OBJECT_PROPS (group, 0, 0, 30);
+
+ c3 = ges_layer_add_asset (layer, asset, 30, 0, 20, GES_TRACK_TYPE_UNKNOWN);
+ c4 = ges_layer_add_asset (layer1, asset, 40, 0, 20, GES_TRACK_TYPE_UNKNOWN);
+ c5 = ges_layer_add_asset (layer2, asset, 50, 0, 20, GES_TRACK_TYPE_UNKNOWN);
+ clips = g_list_prepend (NULL, c3);
+ clips = g_list_prepend (clips, c4);
+ clips = g_list_prepend (clips, c5);
+ group1 = GES_GROUP (ges_container_group (clips));
+ fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group1) == timeline);
+ g_list_free (clips);
+
+ fail_unless (GES_IS_GROUP (group1));
+ CHECK_OBJECT_PROPS (c3, 30, 0, 20);
+ CHECK_OBJECT_PROPS (c4, 40, 0, 20);
+ CHECK_OBJECT_PROPS (c5, 50, 0, 20);
+ CHECK_OBJECT_PROPS (group1, 30, 0, 40);
+ check_layer (c, 0);
+ check_layer (c1, 1);
+ check_layer (c2, 1);
+ check_layer (c3, 0);
+ check_layer (c4, 1);
+ check_layer (c5, 2);
+
+ fail_unless (ges_container_add (GES_CONTAINER (group),
+ GES_TIMELINE_ELEMENT (group1)));
+ CHECK_OBJECT_PROPS (c, 0, 0, 10);
+ CHECK_OBJECT_PROPS (c1, 10, 0, 10);
+ CHECK_OBJECT_PROPS (c2, 20, 0, 10);
+ CHECK_OBJECT_PROPS (c3, 30, 0, 20);
+ CHECK_OBJECT_PROPS (c4, 40, 0, 20);
+ CHECK_OBJECT_PROPS (c5, 50, 0, 20);
+ CHECK_OBJECT_PROPS (group, 0, 0, 70);
+ CHECK_OBJECT_PROPS (group1, 30, 0, 40);
+ check_layer (c, 0);
+ check_layer (c1, 1);
+ check_layer (c2, 1);
+ check_layer (c3, 0);
+ check_layer (c4, 1);
+ check_layer (c5, 2);
+
+ fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group) == timeline);
+ fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group1) == timeline);
+
+ ges_timeline_element_set_start (GES_TIMELINE_ELEMENT (c4), 50);
+ CHECK_OBJECT_PROPS (c, 10, 0, 10);
+ CHECK_OBJECT_PROPS (c1, 20, 0, 10);
+ CHECK_OBJECT_PROPS (c2, 30, 0, 10);
+ CHECK_OBJECT_PROPS (c3, 40, 0, 20);
+ CHECK_OBJECT_PROPS (c4, 50, 0, 20);
+ CHECK_OBJECT_PROPS (c5, 60, 0, 20);
+ CHECK_OBJECT_PROPS (group, 10, 0, 70);
+ CHECK_OBJECT_PROPS (group1, 40, 0, 40);
+ fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group) == timeline);
+ fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group1) == timeline);
+ check_layer (c, 0);
+ check_layer (c1, 1);
+ check_layer (c2, 1);
+ check_layer (c3, 0);
+ check_layer (c4, 1);
+ check_layer (c5, 2);
+
+ /* Our timeline
+ *
+ * L
+ * -----------------------------------------------------------------------
+ * 0------------10-Group-----20---------------30-------Group1----------70
+ * | +-----------+ |+-----------50 |
+ * L1 | | C | || C3 | |
+ * | +-----------+ |+-----------+ |
+ * | | |
+ * --|-------------------------------------------|-----40----------------|
+ * | +------------+ +-------------+ | +--------60 |
+ * L2 | | C1 | | C2 | | | C4 | |
+ * | +------------+ +-------------+ | +--------+ |
+ * --|-------------------------------------------|-----------------------|
+ * | | +--------+|
+ * L3 | | | c5 ||
+ * | | +--------+|
+ * --+-------------------------------------------+-----------------------+
+ */
+ fail_unless (ges_clip_move_to_layer (c, layer1));
+ check_layer (c, 1);
+ check_layer (c1, 2);
+ check_layer (c2, 2);
+ check_layer (c3, 1);
+ check_layer (c4, 2);
+ check_layer (c5, 3);
+ assert_equals_int (_PRIORITY (group), 1);
+ assert_equals_int (_PRIORITY (group1), 1);
+
+ /* We can not move so far! */
+ g_signal_connect_after (c4, "notify::layer",
+ (GCallback) _changed_layer_cb, &nb_layer_notifies);
+ fail_if (ges_clip_move_to_layer (c4, layer));
+ assert_equals_int (nb_layer_notifies, 0);
+ check_layer (c, 1);
+ check_layer (c1, 2);
+ check_layer (c2, 2);
+ check_layer (c3, 1);
+ check_layer (c4, 2);
+ check_layer (c5, 3);
+ assert_equals_int (_PRIORITY (group), 1);
+ assert_equals_int (_PRIORITY (group1), 1);
+
+ clips = ges_container_ungroup (GES_CONTAINER (group), FALSE);
+ assert_equals_int (g_list_length (clips), 4);
+ g_list_free_full (clips, gst_object_unref);
+
+ gst_object_unref (timeline);
+ gst_object_unref (asset);
+}
+
+GST_END_TEST;
+
+static Suite *
+ges_suite (void)
+{
+ Suite *s = suite_create ("ges-group");
+ TCase *tc_chain = tcase_create ("group");
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_move_group);
+ tcase_add_test (tc_chain, test_group_in_group);
+
+ return s;
+}
+
+GST_CHECK_MAIN (ges);
diff --git a/tests/check/ges/test-utils.h b/tests/check/ges/test-utils.h
index 6210dc4..e3f3431 100644
--- a/tests/check/ges/test-utils.h
+++ b/tests/check/ges/test-utils.h
@@ -72,4 +72,16 @@ G_STMT_START { \
#define _INPOINT(obj) GES_TIMELINE_ELEMENT_INPOINT (obj)
#define _PRIORITY(obj) GES_TIMELINE_ELEMENT_PRIORITY (obj)
+#define CHECK_OBJECT_PROPS(obj, start, inpoint, duration) {\
+ assert_equals_uint64 (_START (obj), start);\
+ assert_equals_uint64 (_INPOINT (obj), inpoint);\
+ assert_equals_uint64 (_DURATION (obj), duration);\
+}
+
+#define check_layer(clip, layer_prio) { \
+ GESLayer *tmplayer = ges_clip_get_layer ((clip)); \
+ assert_equals_int (ges_layer_get_priority (tmplayer), (layer_prio)); \
+ gst_object_unref (tmplayer); \
+}
+
#endif /* _GES_TEST_UTILS */
diff --git a/tests/check/ges/timelineedition.c b/tests/check/ges/timelineedition.c
index 3d42d24..f96be28 100644
--- a/tests/check/ges/timelineedition.c
+++ b/tests/check/ges/timelineedition.c
@@ -22,6 +22,33 @@
#include <ges/ges.h>
#include <gst/check/gstcheck.h>
+#define DEEP_CHECK(element, start, inpoint, duration) \
+{ \
+ GList *track_elements, *tmp; \
+ GstClockTime rstart, rinpoint, rduration; \
+ \
+ rstart = ges_timeline_element_get_start (GES_TIMELINE_ELEMENT (element)); \
+ rinpoint = ges_timeline_element_get_inpoint (GES_TIMELINE_ELEMENT (element));\
+ rduration = \
+ ges_timeline_element_get_duration (GES_TIMELINE_ELEMENT (element)); \
+ \
+ assert_equals_uint64 (rstart, start); \
+ assert_equals_uint64 (rinpoint, inpoint); \
+ assert_equals_uint64 (rduration, duration); \
+ \
+ track_elements = GES_CONTAINER_CHILDREN (element); \
+ for (tmp = track_elements; tmp; tmp = tmp->next) { \
+ rstart = ges_timeline_element_get_start (GES_TIMELINE_ELEMENT (tmp->data));\
+ rinpoint = \
+ ges_timeline_element_get_inpoint (GES_TIMELINE_ELEMENT (tmp->data)); \
+ rduration = \
+ ges_timeline_element_get_duration (GES_TIMELINE_ELEMENT (tmp->data)); \
+ assert_equals_uint64 (rstart, start); \
+ assert_equals_uint64 (rinpoint, inpoint); \
+ assert_equals_uint64 (rduration, duration); \
+ } \
+}
+
static gboolean
my_fill_track_func (GESClip * clip,
GESTrackElement * track_element, GstElement * gnlobj, gpointer user_data)
@@ -46,12 +73,6 @@ create_custom_clip (void)
return GES_CLIP (ges_custom_source_clip_new (my_fill_track_func, NULL));
}
-#define CHECK_OBJECT_PROPS(obj, start, inpoint, duration) {\
- assert_equals_uint64 (_START (obj), start);\
- assert_equals_uint64 (_INPOINT (obj), inpoint);\
- assert_equals_uint64 (_DURATION (obj), duration);\
-}
-
GST_START_TEST (test_basic_timeline_edition)
{
GESAsset *asset;
@@ -487,35 +508,6 @@ asset_added_cb (GESProject * project, GESAsset * asset, void *mainloop)
g_main_loop_quit ((GMainLoop *) mainloop);
}
-static void
-deep_check (GESTimelineElement * element, GstClockTime start,
- GstClockTime inpoint, GstClockTime duration)
-{
- GList *track_elements, *tmp;
- GstClockTime rstart, rinpoint, rduration;
-
- rstart = ges_timeline_element_get_start (GES_TIMELINE_ELEMENT (element));
- rinpoint = ges_timeline_element_get_inpoint (GES_TIMELINE_ELEMENT (element));
- rduration =
- ges_timeline_element_get_duration (GES_TIMELINE_ELEMENT (element));
-
- assert_equals_uint64 (rstart, start);
- assert_equals_uint64 (rinpoint, inpoint);
- assert_equals_uint64 (rduration, duration);
-
- track_elements = GES_CONTAINER_CHILDREN (element);
- for (tmp = track_elements; tmp; tmp = tmp->next) {
- rstart = ges_timeline_element_get_start (GES_TIMELINE_ELEMENT (tmp->data));
- rinpoint =
- ges_timeline_element_get_inpoint (GES_TIMELINE_ELEMENT (tmp->data));
- rduration =
- ges_timeline_element_get_duration (GES_TIMELINE_ELEMENT (tmp->data));
- assert_equals_uint64 (rstart, start);
- assert_equals_uint64 (rinpoint, inpoint);
- assert_equals_uint64 (rduration, duration);
- }
-}
-
GST_START_TEST (test_simple_triming)
{
GList *assets;
@@ -556,10 +548,10 @@ GST_START_TEST (test_simple_triming)
element = ges_layer_get_clips (layer)->data;
- deep_check (element, 0, 0, 10);
+ DEEP_CHECK (element, 0, 0, 10);
ges_container_edit (GES_CONTAINER (element), NULL, -1, GES_EDIT_MODE_TRIM,
GES_EDGE_START, 5);
- deep_check (element, 5, 5, 5);
+ DEEP_CHECK (element, 5, 5, 5);
g_main_loop_unref (mainloop);
gst_object_unref (timeline);
@@ -1018,6 +1010,274 @@ GST_START_TEST (test_timeline_edition_mode)
GST_END_TEST;
+GST_START_TEST (test_groups)
+{
+ GESAsset *asset;
+ GESTimeline *timeline;
+ GESGroup *group;
+ GESLayer *layer, *layer1, *layer2, *layer3;
+ GESClip *c, *c1, *c2, *c3, *c4, *c5;
+
+ GList *clips = NULL;
+
+ ges_init ();
+
+ timeline = ges_timeline_new_audio_video ();
+
+ /* Our timeline
+ *
+ * --0------------10-Group-----20---------------30-----------------------70
+ * | +-----------+ |+-----------50 |
+ * L | | C | || C3 | |
+ * | +-----------+ |+-----------+ |
+ * --|-------------------------------------------|-----40----------------|
+ * | +------------+ +-------------+ | +--------60 |
+ * L1 | | C1 | | C2 | | | C4 | |
+ * | +------------+ +-------------+ | +--------+ |
+ * --|-------------------------------------------|-----------------------|
+ * | | +--------+|
+ * L2 | | | c5 ||
+ * | | +--------+|
+ * --+-------------------------------------------+-----------------------+
+ *
+ * L3
+ *
+ * -----------------------------------------------------------------------
+ */
+
+ layer = ges_timeline_append_layer (timeline);
+ layer1 = ges_timeline_append_layer (timeline);
+ layer2 = ges_timeline_append_layer (timeline);
+ layer3 = ges_timeline_append_layer (timeline);
+ assert_equals_int (ges_layer_get_priority (layer3), 3);
+ asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);
+
+ c = ges_layer_add_asset (layer, asset, 0, 0, 10, GES_TRACK_TYPE_UNKNOWN);
+ c1 = ges_layer_add_asset (layer1, asset, 10, 0, 10, GES_TRACK_TYPE_UNKNOWN);
+ c2 = ges_layer_add_asset (layer1, asset, 20, 0, 10, GES_TRACK_TYPE_UNKNOWN);
+ clips = g_list_prepend (clips, c);
+ clips = g_list_prepend (clips, c1);
+ clips = g_list_prepend (clips, c2);
+ group = GES_GROUP (ges_container_group (clips));
+ fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group) == timeline);
+ g_list_free (clips);
+
+ fail_unless (GES_IS_GROUP (group));
+ CHECK_OBJECT_PROPS (c, 0, 0, 10);
+ CHECK_OBJECT_PROPS (c1, 10, 0, 10);
+ CHECK_OBJECT_PROPS (c2, 20, 0, 10);
+ CHECK_OBJECT_PROPS (group, 0, 0, 30);
+
+ c3 = ges_layer_add_asset (layer, asset, 30, 0, 20, GES_TRACK_TYPE_UNKNOWN);
+ c4 = ges_layer_add_asset (layer1, asset, 40, 0, 20, GES_TRACK_TYPE_UNKNOWN);
+ c5 = ges_layer_add_asset (layer2, asset, 50, 0, 20, GES_TRACK_TYPE_UNKNOWN);
+
+ CHECK_OBJECT_PROPS (c3, 30, 0, 20);
+ CHECK_OBJECT_PROPS (c4, 40, 0, 20);
+ CHECK_OBJECT_PROPS (c5, 50, 0, 20);
+ check_layer (c, 0);
+ check_layer (c1, 1);
+ check_layer (c2, 1);
+ check_layer (c3, 0);
+ check_layer (c4, 1);
+ check_layer (c5, 2);
+
+ fail_unless (ges_container_edit (GES_CONTAINER (c), NULL, -1,
+ GES_EDIT_MODE_RIPPLE, GES_EDGE_NONE, 10) == TRUE);
+
+ CHECK_OBJECT_PROPS (c, 10, 0, 10);
+ CHECK_OBJECT_PROPS (c1, 20, 0, 10);
+ CHECK_OBJECT_PROPS (c2, 30, 0, 10);
+ CHECK_OBJECT_PROPS (c3, 40, 0, 20);
+ CHECK_OBJECT_PROPS (c4, 50, 0, 20);
+ CHECK_OBJECT_PROPS (c5, 60, 0, 20);
+ check_layer (c, 0);
+ check_layer (c1, 1);
+ check_layer (c2, 1);
+ check_layer (c3, 0);
+ check_layer (c4, 1);
+ check_layer (c5, 2);
+
+ fail_unless (ges_container_edit (GES_CONTAINER (c), NULL, 1,
+ GES_EDIT_MODE_RIPPLE, GES_EDGE_NONE, 10) == TRUE);
+ CHECK_OBJECT_PROPS (c, 10, 0, 10);
+ CHECK_OBJECT_PROPS (c1, 20, 0, 10);
+ CHECK_OBJECT_PROPS (c2, 30, 0, 10);
+ CHECK_OBJECT_PROPS (c3, 40, 0, 20);
+ CHECK_OBJECT_PROPS (c4, 50, 0, 20);
+ CHECK_OBJECT_PROPS (c5, 60, 0, 20);
+ check_layer (c, 1);
+ check_layer (c1, 2);
+ check_layer (c2, 2);
+ check_layer (c3, 1);
+ check_layer (c4, 2);
+ check_layer (c5, 3);
+
+ fail_unless (ges_container_edit (GES_CONTAINER (c1), NULL, 2,
+ GES_EDIT_MODE_RIPPLE, GES_EDGE_END, 40) == TRUE);
+ CHECK_OBJECT_PROPS (c, 10, 0, 10);
+ CHECK_OBJECT_PROPS (c1, 20, 0, 20);
+ CHECK_OBJECT_PROPS (c2, 40, 0, 10);
+ CHECK_OBJECT_PROPS (c3, 50, 0, 20);
+ CHECK_OBJECT_PROPS (c4, 60, 0, 20);
+ CHECK_OBJECT_PROPS (c5, 70, 0, 20);
+ check_layer (c, 1);
+ check_layer (c1, 2);
+ check_layer (c2, 2);
+ check_layer (c3, 1);
+ check_layer (c4, 2);
+ check_layer (c5, 3);
+
+ fail_unless (ges_container_edit (GES_CONTAINER (c1), NULL, 2,
+ GES_EDIT_MODE_RIPPLE, GES_EDGE_END, 30) == TRUE);
+ CHECK_OBJECT_PROPS (c, 10, 0, 10);
+ CHECK_OBJECT_PROPS (c1, 20, 0, 10);
+ CHECK_OBJECT_PROPS (c2, 30, 0, 10);
+ CHECK_OBJECT_PROPS (c3, 40, 0, 20);
+ CHECK_OBJECT_PROPS (c4, 50, 0, 20);
+ CHECK_OBJECT_PROPS (c5, 60, 0, 20);
+ check_layer (c, 1);
+ check_layer (c1, 2);
+ check_layer (c2, 2);
+ check_layer (c3, 1);
+ check_layer (c4, 2);
+ check_layer (c5, 3);
+
+ fail_unless (ges_container_edit (GES_CONTAINER (c), NULL, 0,
+ GES_EDIT_MODE_RIPPLE, GES_EDGE_NONE, 0) == TRUE);
+ CHECK_OBJECT_PROPS (c, 0, 0, 10);
+ CHECK_OBJECT_PROPS (c1, 10, 0, 10);
+ CHECK_OBJECT_PROPS (c2, 20, 0, 10);
+ CHECK_OBJECT_PROPS (c3, 30, 0, 20);
+ CHECK_OBJECT_PROPS (c4, 40, 0, 20);
+ CHECK_OBJECT_PROPS (c5, 50, 0, 20);
+ check_layer (c, 0);
+ check_layer (c1, 1);
+ check_layer (c2, 1);
+ check_layer (c3, 0);
+ check_layer (c4, 1);
+ check_layer (c5, 2);
+
+ fail_unless (ges_container_edit (GES_CONTAINER (c2), NULL, -1,
+ GES_EDIT_MODE_ROLL, GES_EDGE_END, 40) == TRUE);
+ CHECK_OBJECT_PROPS (c, 0, 0, 10);
+ CHECK_OBJECT_PROPS (c1, 10, 0, 10);
+ CHECK_OBJECT_PROPS (c2, 20, 0, 20);
+ CHECK_OBJECT_PROPS (c3, 40, 10, 10);
+ CHECK_OBJECT_PROPS (c4, 40, 0, 20);
+ CHECK_OBJECT_PROPS (c5, 50, 0, 20);
+ CHECK_OBJECT_PROPS (group, 0, 0, 40);
+ check_layer (c, 0);
+ check_layer (c1, 1);
+ check_layer (c2, 1);
+ check_layer (c3, 0);
+ check_layer (c4, 1);
+ check_layer (c5, 2);
+
+ gst_object_unref (timeline);
+ gst_object_unref (asset);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_snapping_groups)
+{
+ GESAsset *asset;
+ GESTimeline *timeline;
+ GESGroup *group;
+ GESLayer *layer, *layer1, *layer2, *layer3;
+ GESClip *c, *c1, *c2, *c3, *c4, *c5;
+
+ GList *clips = NULL;
+
+ ges_init ();
+
+ timeline = ges_timeline_new_audio_video ();
+ g_object_set (timeline, "snapping-distance", (guint64) 3, NULL);
+
+ /* Our timeline
+ *
+ * --0------------10-Group-----20---------25-----30----------------------70
+ * | +-----------+ | +-----------50 |
+ * L | | C | | | C3 | |
+ * | +-----------+ | +-----------+ |
+ * --|------------------------------------|------------40----------------|
+ * | +------------+ +--------+ +--------60 |
+ * L1 | | C1 | | C2 | | C4 | |
+ * | +------------+ +--------+ +--------+ |
+ * --|------------------------------------+------------------------------|
+ * | +--------+|
+ * L2 | | c5 ||
+ * | +--------+|
+ * --+-------------------------------------------------------------------+
+ *
+ * L3
+ *
+ * -----------------------------------------------------------------------
+ */
+
+ layer = ges_timeline_append_layer (timeline);
+ layer1 = ges_timeline_append_layer (timeline);
+ layer2 = ges_timeline_append_layer (timeline);
+ layer3 = ges_timeline_append_layer (timeline);
+ assert_equals_int (ges_layer_get_priority (layer3), 3);
+ asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);
+
+ c = ges_layer_add_asset (layer, asset, 0, 0, 10, GES_TRACK_TYPE_UNKNOWN);
+ c1 = ges_layer_add_asset (layer1, asset, 10, 0, 10, GES_TRACK_TYPE_UNKNOWN);
+ c2 = ges_layer_add_asset (layer1, asset, 20, 0, 5, GES_TRACK_TYPE_UNKNOWN);
+ clips = g_list_prepend (clips, c);
+ clips = g_list_prepend (clips, c1);
+ clips = g_list_prepend (clips, c2);
+ group = GES_GROUP (ges_container_group (clips));
+ fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group) == timeline);
+ g_list_free (clips);
+
+ fail_unless (GES_IS_GROUP (group));
+ CHECK_OBJECT_PROPS (c, 0, 0, 10);
+ CHECK_OBJECT_PROPS (c1, 10, 0, 10);
+ CHECK_OBJECT_PROPS (c2, 20, 0, 5);
+ CHECK_OBJECT_PROPS (group, 0, 0, 25);
+
+ c3 = ges_layer_add_asset (layer, asset, 30, 0, 20, GES_TRACK_TYPE_UNKNOWN);
+ c4 = ges_layer_add_asset (layer1, asset, 40, 0, 20, GES_TRACK_TYPE_UNKNOWN);
+ c5 = ges_layer_add_asset (layer2, asset, 50, 0, 20, GES_TRACK_TYPE_UNKNOWN);
+
+ CHECK_OBJECT_PROPS (c3, 30, 0, 20);
+ CHECK_OBJECT_PROPS (c4, 40, 0, 20);
+ CHECK_OBJECT_PROPS (c5, 50, 0, 20);
+ check_layer (c, 0);
+ check_layer (c1, 1);
+ check_layer (c2, 1);
+ check_layer (c3, 0);
+ check_layer (c4, 1);
+ check_layer (c5, 2);
+
+ /* c2 should snap with C3 and make the group moving to 5 */
+ fail_unless (ges_container_edit (GES_CONTAINER (c), NULL, -1,
+ GES_EDIT_MODE_NORMAL, GES_EDGE_NONE, 3) == TRUE);
+
+ DEEP_CHECK (c, 5, 0, 10);
+ DEEP_CHECK (c1, 15, 0, 10);
+ DEEP_CHECK (c2, 25, 0, 5);
+ DEEP_CHECK (c2, 25, 0, 5);
+ DEEP_CHECK (c4, 40, 0, 20);
+ DEEP_CHECK (c5, 50, 0, 20);
+ CHECK_OBJECT_PROPS (group, 5, 0, 25);
+ check_layer (c, 0);
+ check_layer (c1, 1);
+ check_layer (c2, 1);
+ check_layer (c3, 0);
+ check_layer (c4, 1);
+ check_layer (c5, 2);
+
+
+ gst_object_unref (timeline);
+ gst_object_unref (asset);
+}
+
+GST_END_TEST;
+
static Suite *
ges_suite (void)
{
@@ -1030,6 +1290,8 @@ ges_suite (void)
tcase_add_test (tc_chain, test_snapping);
tcase_add_test (tc_chain, test_timeline_edition_mode);
tcase_add_test (tc_chain, test_simple_triming);
+ tcase_add_test (tc_chain, test_groups);
+ tcase_add_test (tc_chain, test_snapping_groups);
return s;
}