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.
This commit is contained in:
Thibault Saunier 2013-06-26 17:08:57 -04:00
parent d3e3871440
commit a933d9540d
20 changed files with 1620 additions and 118 deletions

View file

@ -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>

View file

@ -1091,6 +1091,23 @@ GES_IS_URI_CLIP_ASSET_CLASS
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>

View file

@ -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 = \

View file

@ -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.",

View file

@ -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);

View file

@ -44,6 +44,7 @@ typedef enum
GES_CHILDREN_UPDATE,
GES_CHILDREN_IGNORE_NOTIFIES,
GES_CHILDREN_UPDATE_OFFSETS,
GES_CHILDREN_LAST
} GESChildrenControlMode;
/**

550
ges/ges-group.c Normal file
View file

@ -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;
}

56
ges/ges-group.h Normal file
View file

@ -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 */

View file

@ -83,6 +83,14 @@ timeline_move_object (GESTimeline *timeline, GESTrackElement * object,
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);

View file

@ -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)) {

View file

@ -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));
if (new_layer == NULL) {
do {
new_layer = ges_timeline_append_layer (timeline);
} while (ges_layer_get_priority (new_layer) < prio + offset);
}
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);
}
ret &= ges_clip_move_to_layer (key, new_layer);
gst_object_unref (layer);
}
/* 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)) {

View file

@ -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;

View file

@ -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;

View file

@ -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>

View file

@ -41,6 +41,7 @@ check_PROGRAMS = \
ges/overlays\
ges/text_properties\
ges/mixers\
ges/group\
ges/project
noinst_LTLIBRARIES=$(testutils_noisnt_libraries)

View file

@ -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));

View file

@ -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);

500
tests/check/ges/group.c Normal file
View file

@ -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);

View file

@ -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 */

View file

@ -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;
}