Merge remote-tracking branch 'origin/0.10'

Conflicts:
	bindings/python/ges-types.defs
	bindings/python/ges.defs
	bindings/python/ges.override
	configure.ac
	ges/ges-timeline.c
This commit is contained in:
Thibault Saunier 2012-04-25 17:53:38 -04:00
commit b1a8596256
18 changed files with 3153 additions and 176 deletions

View file

@ -30,6 +30,8 @@ GESTextVAlign
DEFAULT_VALIGNMENT
GESVideoTestPattern
GESPipelineFlags
GESEdge
GESEditMode
<SUBSECTION Standard>
GES_TYPE_TRACK_TYPE
ges_track_type_get_type
@ -104,6 +106,8 @@ ges_track_object_set_child_property_by_pspec
ges_track_object_get_child_property
ges_track_object_get_child_property_valist
ges_track_object_get_child_property_by_pspec
ges_track_object_edit
ges_track_object_copy
<SUBSECTION Standard>
GES_TRACK_OBJECT_DURATION
GES_TRACK_OBJECT_INPOINT
@ -321,6 +325,12 @@ ges_timeline_object_set_top_effect_priority
ges_timeline_object_set_supported_formats
ges_timeline_object_get_supported_formats
ges_timeline_object_split
ges_timeline_object_edit
ges_timeline_object_ripple
ges_timeline_object_ripple_end
ges_timeline_object_roll_start
ges_timeline_object_roll_end
ges_timeline_object_trim_start
<SUBSECTION Standard>
GES_TIMELINE_OBJECT_DURATION
GES_TIMELINE_OBJECT_INPOINT

View file

@ -18,6 +18,11 @@
* Boston, MA 02111-1307, USA.
*/
/**
* SECTION:ges-enums
* @short_description: Various enums for the Gstreamer Editing Services
*/
#include "ges-enums.h"
#define C_ENUM(v) ((guint) v)
@ -74,6 +79,64 @@ ges_pipeline_flags_get_type (void)
return id;
}
static void
register_ges_edit_mode (GType * id)
{
static const GEnumValue edit_mode[] = {
{C_ENUM (GES_EDIT_MODE_NORMAL), "GES_EDIT_MODE_NORMAL",
"edit_normal"},
{C_ENUM (GES_EDIT_MODE_RIPPLE), "GES_EDIT_MODE_RIPPLE",
"edit_ripple"},
{C_ENUM (GES_EDIT_MODE_ROLL), "GES_EDIT_MODE_ROLL",
"edit_roll"},
{C_ENUM (GES_EDIT_MODE_TRIM), "GES_EDIT_MODE_TRIM",
"edit_trim"},
{C_ENUM (GES_EDIT_MODE_SLIDE), "GES_EDIT_MODE_SLIDE",
"edit_slide"},
{0, NULL, NULL}
};
*id = g_enum_register_static ("GESEditMode", edit_mode);
}
GType
ges_edit_mode_get_type (void)
{
static GType id;
static GOnce once = G_ONCE_INIT;
g_once (&once, (GThreadFunc) register_ges_edit_mode, &id);
return id;
}
static void
register_ges_edge (GType * id)
{
static const GEnumValue edges[] = {
{C_ENUM (GES_EDGE_START), "GES_EDGE_START", "edge_start"},
{C_ENUM (GES_EDGE_END), "GES_EDGE_END", "edge_end"},
{C_ENUM (GES_EDGE_NONE), "GES_EDGE_NONE", "edge_none"},
{0, NULL, NULL}
};
*id = g_enum_register_static ("GESEdge", edges);
}
GType
ges_edge_get_type (void)
{
static GType id;
static GOnce once = G_ONCE_INIT;
g_once (&once, (GThreadFunc) register_ges_edge, &id);
return id;
}
static GEnumValue transition_types[] = {
{
0,

View file

@ -323,6 +323,70 @@ typedef enum {
GType ges_pipeline_flags_get_type (void);
/**
* GESEditMode:
* @GES_EDIT_MODE_NORMAL: The object is edited the normal way (default).
* @GES_EDIT_MODE_RIPPLE: The objects are edited in ripple mode.
* The Ripple mode allows you to modify the beginning/end of a clip
* and move the neighbours accordingly. This will change the overall
* timeline duration. In the case of ripple end, the duration of the
* clip being rippled can't be supperior to it max-duration - inpoint
* and if it would be the case, nothing wil happen.
* @GES_EDIT_MODE_ROLL: The object is edited in roll mode.
* The Roll mode allows you to modify the position of an editing point
* between two clips without modifying the inpoint of the first clip
* nor the out-point of the second clip. This will not change the
* overall timeline duration. In the case of ripple end, the duration of the
* clip being rolled can't be supperior to it max-duration - inpoint and if
* it would be the case, nothing wil happen.
* @GES_EDIT_MODE_TRIM: The object is edited in trim mode.
* The Trim mode allows you to modify the in-point/out-point of a clip without
* modifying it's duration or position in the timeline. A clip being trim
* @GES_EDIT_MODE_SLIDE: The object is edited in slide mode.
* The Slide mode allows you to modify the position of a clip in a
* timeline without modifying it's duration or it's in-point, but will
* modify the out-point of the previous clip and in-point of the
* following clip so as not to modify the overall timeline duration.
* (not implemented yet)
*
* The various edition modes a clip can be edited with.
*
*
* You can also find more explanation about the behaviour of those modes at:
* <ulink url="http://pitivi.org/manual/trimming.html"> trim, ripple and roll</ulink>
* and <ulink url="http://pitivi.org/manual/usingclips.html">clip management</ulink>.
*/
typedef enum {
GES_EDIT_MODE_NORMAL,
GES_EDIT_MODE_RIPPLE,
GES_EDIT_MODE_ROLL,
GES_EDIT_MODE_TRIM,
GES_EDIT_MODE_SLIDE
} GESEditMode;
#define GES_TYPE_EDIT_MODE ges_edit_mode_get_type()
GType ges_edit_mode_get_type (void);
/**
* GESEdge:
* @GES_EDGE_START: Represents the start of an object.
* @GES_EDGE_END: Represents the end of an object.
* @GES_EDGE_NONE: Represent the fact we are not workin with any edge of an
* object.
*
* The edges of an object contain in a #GESTimeline or #GESTrack
*/
typedef enum {
GES_EDGE_START,
GES_EDGE_END,
GES_EDGE_NONE
} GESEdge;
#define GES_TYPE_EDGE ges_edge_get_type()
GType ges_edge_get_type (void);
G_END_DECLS
#endif /* __GES_ENUMS_H__ */

View file

@ -22,8 +22,41 @@
#define __GES_INTERNAL_H__
#include <gst/gst.h>
#include "ges-timeline.h"
#include "ges-track-object.h"
GST_DEBUG_CATEGORY_EXTERN (_ges_debug);
#define GST_CAT_DEFAULT _ges_debug
gboolean
timeline_ripple_object (GESTimeline *timeline, GESTrackObject *obj,
GList * layers, GESEdge edge,
guint64 position);
gboolean
timeline_slide_object (GESTimeline *timeline, GESTrackObject *obj,
GList * layers, GESEdge edge, guint64 position);
gboolean
timeline_roll_object (GESTimeline *timeline, GESTrackObject *obj,
GList * layers, GESEdge edge, guint64 position);
gboolean
timeline_trim_object (GESTimeline *timeline, GESTrackObject * object,
GList * layers, GESEdge edge, guint64 position);
gboolean
ges_timeline_trim_object_simple (GESTimeline * timeline, GESTrackObject * obj,
GList * layers, GESEdge edge, guint64 position, gboolean snapping);
gboolean
ges_timeline_move_object_simple (GESTimeline * timeline, GESTrackObject * object,
GList * layers, GESEdge edge, guint64 position);
gboolean
timeline_move_object (GESTimeline *timeline, GESTrackObject * object,
GList * layers, GESEdge edge, guint64 position);
gboolean
timeline_context_to_layer (GESTimeline *timeline, gint offset);
#endif /* __GES_INTERNAL_H__ */

View file

@ -231,7 +231,8 @@ filesource_set_max_duration (GESTimelineObject * object, guint64 maxduration)
tckobjs = ges_timeline_object_get_track_objects (object);
for (tmp = tckobjs; tmp; tmp = g_list_next (tmp)) {
g_object_set (tmp->data, "max-duration", maxduration, NULL);
ges_track_object_set_max_duration (GES_TRACK_OBJECT (tmp->data),
maxduration);
}
g_list_free_full (tckobjs, g_object_unref);

View file

@ -623,7 +623,6 @@ static void
track_object_added_cb (GESTrack * track, GESTrackObject * track_object,
GESTimelineLayer * layer)
{
GST_ERROR ("TRACKOBJECTADDED %i", GES_IS_TRACK_SOURCE (track_object));
if (GES_IS_TRACK_SOURCE (track_object)) {
g_signal_connect (G_OBJECT (track_object), "notify::start",
G_CALLBACK (track_object_changed_cb), NULL);

View file

@ -59,10 +59,6 @@ track_object_priority_changed_cb (GESTrackObject * child,
GParamSpec * arg G_GNUC_UNUSED, GESTimelineObject * object);
static void update_height (GESTimelineObject * object);
void
tck_object_added_cb (GESTimelineObject * object,
GESTrackObject * track_object, GList * track_objects);
static gint sort_track_effects (gpointer a, gpointer b,
GESTimelineObject * object);
static void
@ -398,6 +394,7 @@ ges_timeline_object_class_init (GESTimelineObjectClass * klass)
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
klass->need_fill_track = TRUE;
klass->snaps = FALSE;
}
static void
@ -478,6 +475,7 @@ ges_timeline_object_create_track_objects (GESTimelineObject * object,
GST_WARNING ("no GESTimelineObject::create_track_objects implentation");
return FALSE;
}
return klass->create_track_objects (object, track);
}
@ -609,7 +607,7 @@ ges_timeline_object_add_track_object (GESTimelineObject * object, GESTrackObject
g_signal_connect (G_OBJECT (trobj), "notify::duration",
G_CALLBACK (track_object_duration_changed_cb), object);
mapping->inpoint_notifyid =
g_signal_connect (G_OBJECT (trobj), "notify::inpoint",
g_signal_connect (G_OBJECT (trobj), "notify::in-point",
G_CALLBACK (track_object_inpoint_changed_cb), object);
mapping->priority_notifyid =
g_signal_connect (G_OBJECT (trobj), "notify::priority",
@ -647,12 +645,13 @@ ges_timeline_object_release_track_object (GESTimelineObject * object,
{
GList *tmp;
ObjectMapping *mapping = NULL;
GESTimelineObjectClass *klass = GES_TIMELINE_OBJECT_GET_CLASS (object);
GESTimelineObjectClass *klass;
g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
g_return_val_if_fail (GES_IS_TRACK_OBJECT (trackobject), FALSE);
GST_DEBUG ("object:%p, trackobject:%p", object, trackobject);
klass = GES_TIMELINE_OBJECT_GET_CLASS (object);
if (!(g_list_find (object->priv->trackobjects, trackobject))) {
GST_WARNING ("TrackObject isn't controlled by this object");
@ -771,12 +770,21 @@ ges_timeline_object_set_start_internal (GESTimelineObject * object,
GList *tmp;
GESTrackObject *tr;
ObjectMapping *map;
GESTimeline *timeline = NULL;
GESTimelineObjectPrivate *priv = object->priv;
gboolean snap = FALSE;
g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
GST_DEBUG ("object:%p, start:%" GST_TIME_FORMAT,
object, GST_TIME_ARGS (start));
/* If the class has snapping enabled and the object is in a timeline,
* we snap */
if (priv->layer && GES_TIMELINE_OBJECT_GET_CLASS (object)->snaps)
timeline = ges_timeline_layer_get_timeline (object->priv->layer);
snap = timeline && priv->initiated_move == NULL ? TRUE : FALSE;
object->priv->ignore_notifies = TRUE;
for (tmp = object->priv->trackobjects; tmp; tmp = g_list_next (tmp)) {
@ -793,7 +801,12 @@ ges_timeline_object_set_start_internal (GESTimelineObject * object,
continue;
}
ges_track_object_set_start (tr, new_start);
/* Make the snapping happen if in a timeline */
if (snap)
ges_timeline_move_object_simple (timeline, tr, NULL, GES_EDGE_NONE,
start);
else
ges_track_object_set_start (tr, start);
} else {
/* ... or update the offset */
map->start_offset = start - tr->start;
@ -812,6 +825,9 @@ ges_timeline_object_set_start_internal (GESTimelineObject * object,
* @start: the position in #GstClockTime
*
* Set the position of the object in its containing layer
*
* Note that if the timeline snap-distance property of the timeline containing
* @object is set, @object will properly snap to its neighboors.
*/
void
ges_timeline_object_set_start (GESTimelineObject * object, guint64 start)
@ -873,19 +889,37 @@ ges_timeline_object_set_duration_internal (GESTimelineObject * object,
{
GList *tmp;
GESTrackObject *tr;
GESTimeline *timeline = NULL;
GESTimelineObjectPrivate *priv = object->priv;
gboolean snap = FALSE;
g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
GST_DEBUG ("object:%p, duration:%" GST_TIME_FORMAT,
object, GST_TIME_ARGS (duration));
if (priv->layer && GES_TIMELINE_OBJECT_GET_CLASS (object)->snaps)
timeline = ges_timeline_layer_get_timeline (object->priv->layer);
/* If the class has snapping enabled, the object is in a timeline,
* and we are not following a moved TrackObject, we snap */
snap = timeline && priv->initiated_move == NULL ? TRUE : FALSE;
object->priv->ignore_notifies = TRUE;
for (tmp = object->priv->trackobjects; tmp; tmp = g_list_next (tmp)) {
tr = (GESTrackObject *) tmp->data;
if (ges_track_object_is_locked (tr))
/* call set_duration on each trackobject */
ges_track_object_set_duration (tr, duration);
if (ges_track_object_is_locked (tr)) {
/* call set_duration on each trackobject
* and make the snapping happen if in a timeline */
if (G_LIKELY (snap))
ges_timeline_trim_object_simple (timeline, tr, NULL, GES_EDGE_END,
tr->start + duration, TRUE);
else
ges_track_object_set_duration (tr, duration);
}
}
object->priv->ignore_notifies = FALSE;
object->duration = duration;
return TRUE;
@ -897,6 +931,9 @@ ges_timeline_object_set_duration_internal (GESTimelineObject * object,
* @duration: the duration in #GstClockTime
*
* Set the duration of the object
*
* Note that if the timeline snap-distance property of the timeline containing
* @object is set, @object will properly snap to its neighboors.
*/
void
ges_timeline_object_set_duration (GESTimelineObject * object, guint64 duration)
@ -924,12 +961,10 @@ ges_timeline_object_set_priority_internal (GESTimelineObject * object,
GST_DEBUG ("object:%p, priority:%" G_GUINT32_FORMAT, object, priority);
priv = object->priv;
priv->ignore_notifies = TRUE;
object->priv->ignore_notifies = TRUE;
get_layer_priorities (priv->layer, &layer_min_gnl_prio, &layer_max_gnl_prio);
priv->ignore_notifies = TRUE;
for (tmp = priv->trackobjects; tmp; tmp = g_list_next (tmp)) {
tr = (GESTrackObject *) tmp->data;
map = find_object_mapping (object, tr);
@ -978,7 +1013,7 @@ void
ges_timeline_object_set_moving_from_layer (GESTimelineObject * object,
gboolean is_moving)
{
g_return_if_fail (GES_IS_TRACK_OBJECT (object));
g_return_if_fail (GES_IS_TIMELINE_OBJECT (object));
object->priv->is_moving = is_moving;
}
@ -998,6 +1033,8 @@ ges_timeline_object_set_moving_from_layer (GESTimelineObject * object,
gboolean
ges_timeline_object_is_moving_from_layer (GESTimelineObject * object)
{
g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
return object->priv->is_moving;
}
@ -1305,38 +1342,78 @@ ges_timeline_object_set_top_effect_priority (GESTimelineObject * object,
return TRUE;
}
void
tck_object_added_cb (GESTimelineObject * object,
GESTrackObject * track_object, GList * track_objects)
/**
* ges_timeline_object_edit:
* @object: the #GESTimelineObject to edit
* @layers: (element-type GESTimelineLayer): The layers you want the edit to
* happen in, %NULL means that the edition is done in all the
* #GESTimelineLayers contained in the current timeline.
* @new_layer_priority: The priority of the layer @object should land in.
* If the layer you're trying to move the object to doesn't exist, it will
* be created automatically. -1 means no move.
* @mode: The #GESEditMode in which the editition will happen.
* @edge: The #GESEdge the edit should happen on.
* @position: The position at which to edit @object (in nanosecond)
*
* Edit @object in the different exisiting #GESEditMode modes. In the case of
* slide, and roll, you need to specify a #GESEdge
*
* Returns: %TRUE if the object as been edited properly, %FALSE if an error
* occured
*
* Since: 0.10.XX
*/
gboolean
ges_timeline_object_edit (GESTimelineObject * object, GList * layers,
gint new_layer_priority, GESEditMode mode, GESEdge edge, guint64 position)
{
gint64 duration, start, inpoint, position;
GList *tmp;
gboolean locked;
gboolean ret = TRUE;
GESTimelineLayer *layer;
ges_track_object_set_locked (track_object, FALSE);
g_object_get (object, "start", &position, NULL);
for (tmp = track_objects; tmp; tmp = tmp->next) {
if (ges_track_object_get_track (track_object)->type ==
ges_track_object_get_track (tmp->data)->type) {
locked = ges_track_object_is_locked (tmp->data);
ges_track_object_set_locked (tmp->data, FALSE);
g_object_get (tmp->data, "duration", &duration, "start", &start,
"in-point", &inpoint, NULL);
g_object_set (tmp->data, "duration",
duration - (duration + start - position), NULL);
g_object_set (track_object, "start", position, "in-point",
duration - (duration + start - inpoint - position), "duration",
duration + start - position, NULL);
ges_track_object_set_locked (tmp->data, locked);
ges_track_object_set_locked (track_object, locked);
g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
if (!G_UNLIKELY (object->priv->trackobjects)) {
GST_WARNING_OBJECT (object, "Trying to edit, but not containing"
"any TrackObject yet.");
return FALSE;
} else if (position < 0) {
GST_DEBUG_OBJECT (object, "Trying to move before 0, not moving");
}
for (tmp = object->priv->trackobjects; tmp; tmp = g_list_next (tmp)) {
if (ges_track_object_is_locked (tmp->data)) {
ret &= ges_track_object_edit (tmp->data, layers, mode, edge, position);
break;
}
}
/* Moving to layer */
if (new_layer_priority == -1) {
GST_DEBUG_OBJECT (object, "Not moving new prio %d", new_layer_priority);
} else {
gint priority_offset;
layer = object->priv->layer;
if (layer == NULL) {
GST_WARNING_OBJECT (object, "Not in any layer yet, not moving");
return FALSE;
}
priority_offset = new_layer_priority -
ges_timeline_layer_get_priority (layer);
ret &= timeline_context_to_layer (layer->timeline, priority_offset);
}
return ret;
}
/**
* ges_timeline_object_split:
* @object: the #GESTimelineObject to split
* @position: The position at which to split the @object (in nanosecond)
* @position: a #GstClockTime representing the position at which to split
* @object
*
* The function modifies @object, and creates another #GESTimelineObject so
* we have two clips at the end, splitted at the time specified by @position.
@ -1347,40 +1424,103 @@ tck_object_added_cb (GESTimelineObject * object,
* Since: 0.10.XX
*/
GESTimelineObject *
ges_timeline_object_split (GESTimelineObject * object, gint64 position)
ges_timeline_object_split (GESTimelineObject * object, guint64 position)
{
GList *track_objects, *tmp;
GESTimelineLayer *layer;
GList *tmp;
gboolean locked;
GESTimelineObject *new_object;
gint64 duration, start, inpoint;
GESTimelineObjectPrivate *priv;
GstClockTime start, inpoint, duration;
g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), NULL);
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (position), NULL);
g_object_get (object, "duration", &duration, "start", &start, "in-point",
&inpoint, NULL);
priv = object->priv;
track_objects = ges_timeline_object_get_track_objects (object);
layer = ges_timeline_object_get_layer (object);
duration = GES_TIMELINE_OBJECT_DURATION (object);
start = GES_TIMELINE_OBJECT_START (object);
inpoint = GES_TIMELINE_OBJECT_INPOINT (object);
new_object = ges_timeline_object_copy (object, FALSE);
if (g_list_length (track_objects) == 2) {
g_object_set (new_object, "start", position, NULL);
g_signal_connect (G_OBJECT (new_object), "track-object-added",
G_CALLBACK (tck_object_added_cb), track_objects);
} else {
for (tmp = track_objects; tmp; tmp = tmp->next) {
g_object_set (tmp->data, "duration",
duration - (duration + start - position), NULL);
g_object_set (new_object, "start", position, "in-point",
duration - (duration + start - position), "duration",
(duration + start - position), NULL);
g_object_set (object, "duration",
duration - (duration + start - position), NULL);
}
if (position >= start + duration || position <= start) {
GST_WARNING_OBJECT (object, "Can not split %" GST_TIME_FORMAT
" out of boundaries", GST_TIME_ARGS (position));
return NULL;
}
ges_timeline_layer_add_object (layer, new_object);
GST_DEBUG_OBJECT (object, "Spliting at %" GST_TIME_FORMAT,
GST_TIME_ARGS (position));
/* Create the new TimelineObject */
new_object = ges_timeline_object_copy (object, FALSE);
/* Set new timing properties on the TimelineObject */
ges_timeline_object_set_start (new_object, position);
ges_timeline_object_set_inpoint (new_object, object->inpoint +
duration - (duration + start - position));
ges_timeline_object_set_duration (new_object, duration + start - position);
if (object->priv->layer) {
/* We do not want the timeline to create again TrackObject-s */
ges_timeline_object_set_moving_from_layer (new_object, TRUE);
ges_timeline_layer_add_object (object->priv->layer, new_object);
ges_timeline_object_set_moving_from_layer (new_object, FALSE);
}
/* We first set the new duration and the child mapping will be updated
* properly in the following loop */
object->duration = position - object->start;
for (tmp = priv->trackobjects; tmp; tmp = tmp->next) {
GESTrack *track;
GESTrackObject *new_tckobj, *tckobj = GES_TRACK_OBJECT (tmp->data);
duration = ges_track_object_get_duration (tckobj);
start = ges_track_object_get_start (tckobj);
inpoint = ges_track_object_get_inpoint (tckobj);
if (position <= start || position >= (start + duration)) {
GST_DEBUG_OBJECT (tckobj, "Outside %" GST_TIME_FORMAT "the boundaries "
"not copying it ( start %" GST_TIME_FORMAT ", end %" GST_TIME_FORMAT
")", GST_TIME_ARGS (position), GST_TIME_ARGS (tckobj->start),
GST_TIME_ARGS (tckobj->start + tckobj->duration));
continue;
}
new_tckobj = ges_track_object_copy (tckobj, TRUE);
if (new_tckobj == NULL) {
GST_WARNING_OBJECT (tckobj, "Could not create a copy");
continue;
}
ges_timeline_object_add_track_object (new_object, new_tckobj);
track = ges_track_object_get_track (tckobj);
if (track == NULL)
GST_DEBUG_OBJECT (tckobj, "Was not in a track, not adding %p to"
"any track", new_tckobj);
else
ges_track_add_object (track, new_tckobj);
/* Unlock TrackObject-s as we do not want the container to move
* syncronously */
locked = ges_track_object_is_locked (tckobj);
ges_track_object_set_locked (new_tckobj, FALSE);
ges_track_object_set_locked (tckobj, FALSE);
/* Set 'new' track object timing propeties */
ges_track_object_set_start (new_tckobj, position);
ges_track_object_set_inpoint (new_tckobj, inpoint + duration - (duration +
start - position));
ges_track_object_set_duration (new_tckobj, duration + start - position);
/* Set 'old' track object duration */
ges_track_object_set_duration (tckobj, position - start);
/* And let track objects in the same locking state as before. */
ges_track_object_set_locked (tckobj, locked);
ges_track_object_set_locked (new_tckobj, locked);
}
return new_object;
}
@ -1414,18 +1554,6 @@ ges_timeline_object_copy (GESTimelineObject * object, gboolean * deep)
ret = g_object_newv (G_TYPE_FROM_INSTANCE (object), n_params, params);
if (GES_IS_TIMELINE_FILE_SOURCE (ret)) {
GList *tck_objects;
tck_objects = ges_timeline_object_get_track_objects (object);
if (g_list_length (tck_objects) == 1) {
GESTrackType type;
type = ges_track_object_get_track (tck_objects->data)->type;
ges_timeline_filesource_set_supported_formats (GES_TIMELINE_FILE_SOURCE
(ret), type);
}
g_list_free (tck_objects);
}
g_free (specs);
g_free (params);
@ -1531,6 +1659,264 @@ ges_timeline_object_set_max_duration (GESTimelineObject * object,
klass->set_max_duration (object, maxduration);
}
/**
* ges_timeline_object_ripple:
* @object: The #GESTimeline to ripple.
* @start: The new start of @object in ripple mode.
*
* Edits @object in ripple mode. It allows you to modify the
* start of @object and move the following neighbours accordingly.
* This will change the overall timeline duration.
*
* You could also use:
*
* #ges_timeline_object_edit (@object, @layers,
* new_layer_priority=-1, GES_EDIT_MODE_RIPPLE, GES_EDGE_NONE,
* @position);
*
* Which lets you more control over layer management.
*
* Returns: %TRUE if the object as been rippled properly, %FALSE if an error
* occured
*/
gboolean
ges_timeline_object_ripple (GESTimelineObject * object, guint64 start)
{
GList *tmp, *tckobjs;
gboolean ret = TRUE;
GESTimeline *timeline;
g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
timeline = ges_timeline_layer_get_timeline (object->priv->layer);
if (timeline == NULL) {
GST_DEBUG ("Not in a timeline yet");
return FALSE;
}
tckobjs = ges_timeline_object_get_track_objects (object);
for (tmp = tckobjs; tmp; tmp = g_list_next (tmp)) {
if (ges_track_object_is_locked (tmp->data)) {
ret = timeline_ripple_object (timeline, GES_TRACK_OBJECT (tmp->data),
NULL, GES_EDGE_NONE, start);
/* As we work only with locked objects, the changes will be reflected
* to others controlled TrackObjects */
break;
}
}
g_list_free_full (tckobjs, g_object_unref);
return ret;
}
/**
* ges_timeline_object_ripple_end:
* @object: The #GESTimeline to ripple.
* @end: The new end (start + duration) of @object in ripple mode. It will
* basically only change the duration of @object.
*
* Edits @object in ripple mode. It allows you to modify the
* duration of a @object and move the following neighbours accordingly.
* This will change the overall timeline duration.
*
* You could also use:
*
* #ges_timeline_object_edit (@object, @layers,
* new_layer_priority=-1, GES_EDIT_MODE_RIPPLE, GES_EDGE_END, @end);
*
* Which lets you more control over layer management.
*
* Returns: %TRUE if the object as been rippled properly, %FALSE if an error
* occured
*/
gboolean
ges_timeline_object_ripple_end (GESTimelineObject * object, guint64 end)
{
GList *tmp, *tckobjs;
gboolean ret = TRUE;
GESTimeline *timeline;
g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
timeline = ges_timeline_layer_get_timeline (object->priv->layer);
if (timeline == NULL) {
GST_DEBUG ("Not in a timeline yet");
return FALSE;
}
tckobjs = ges_timeline_object_get_track_objects (object);
for (tmp = tckobjs; tmp; tmp = g_list_next (tmp)) {
if (ges_track_object_is_locked (tmp->data)) {
ret = timeline_ripple_object (timeline, GES_TRACK_OBJECT (tmp->data),
NULL, GES_EDGE_END, end);
/* As we work only with locked objects, the changes will be reflected
* to others controlled TrackObjects */
break;
}
}
g_list_free_full (tckobjs, g_object_unref);
return ret;
}
/**
* ges_timeline_object_roll_start:
* @start: The new start of @object in roll mode, it will also adapat
* the in-point of @object according
*
* Edits @object in roll mode. It allows you to modify the
* start and inpoint of a @object and "resize" (basicly change the duration
* in this case) of the previous neighbours accordingly.
* This will not change the overall timeline duration.
*
* You could also use:
*
* #ges_timeline_object_edit (@object, @layers,
* new_layer_priority=-1, GES_EDIT_MODE_ROLL, GES_EDGE_START, @start);
*
* Which lets you more control over layer management.
*
* Returns: %TRUE if the object as been roll properly, %FALSE if an error
* occured
*/
gboolean
ges_timeline_object_roll_start (GESTimelineObject * object, guint64 start)
{
GList *tmp, *tckobjs;
gboolean ret = TRUE;
GESTimeline *timeline;
g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
timeline = ges_timeline_layer_get_timeline (object->priv->layer);
if (timeline == NULL) {
GST_DEBUG ("Not in a timeline yet");
return FALSE;
}
tckobjs = ges_timeline_object_get_track_objects (object);
for (tmp = tckobjs; tmp; tmp = g_list_next (tmp)) {
if (ges_track_object_is_locked (tmp->data)) {
ret = timeline_roll_object (timeline, GES_TRACK_OBJECT (tmp->data),
NULL, GES_EDGE_START, start);
/* As we work only with locked objects, the changes will be reflected
* to others controlled TrackObjects */
break;
}
}
g_list_free_full (tckobjs, g_object_unref);
return ret;
}
/**
* ges_timeline_object_roll_end:
* @object: The #GESTimeline to roll.
* @end: The new end (start + duration) of @object in roll mode
*
* Edits @object in roll mode. It allows you to modify the
* duration of a @object and trim (basicly change the start + inpoint
* in this case) the following neighbours accordingly.
* This will not change the overall timeline duration.
*
* You could also use:
*
* #ges_timeline_object_edit (@object, @layers,
* new_layer_priority=-1, GES_EDIT_MODE_ROLL, GES_EDGE_END, @end);
*
* Which lets you more control over layer management.
*
* Returns: %TRUE if the object as been rolled properly, %FALSE if an error
* occured
*/
gboolean
ges_timeline_object_roll_end (GESTimelineObject * object, guint64 end)
{
GList *tmp, *tckobjs;
gboolean ret = TRUE;
GESTimeline *timeline;
g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
timeline = ges_timeline_layer_get_timeline (object->priv->layer);
if (timeline == NULL) {
GST_DEBUG ("Not in a timeline yet");
return FALSE;
}
tckobjs = ges_timeline_object_get_track_objects (object);
for (tmp = tckobjs; tmp; tmp = g_list_next (tmp)) {
if (ges_track_object_is_locked (tmp->data)) {
ret = timeline_roll_object (timeline, GES_TRACK_OBJECT (tmp->data),
NULL, GES_EDGE_END, end);
/* As we work only with locked objects, the changes will be reflected
* to others controlled TrackObjects */
break;
}
}
g_list_free_full (tckobjs, g_object_unref);
return ret;
}
/**
* ges_timeline_object_trim_start:
* @object: The #GESTimeline to trim.
* @start: The new start of @object in trim mode, will adapt the inpoint
* of @object accordingly
*
* Edits @object in trim mode. It allows you to modify the
* inpoint and start of @object.
* This will not change the overall timeline duration.
*
* You could also use:
*
* #ges_timeline_object_edit (@object, @layers,
* new_layer_priority=-1, GES_EDIT_MODE_TRIM, GES_EDGE_START, @start);
*
* Which lets you more control over layer management.
*
* Note that to trim the end of an object you can just set its duration. The same way
* as this method, it will take into account the snapping-distance property of the
* timeline in which @object is.
*
* Returns: %TRUE if the object as been trimmed properly, %FALSE if an error
* occured
*/
gboolean
ges_timeline_object_trim_start (GESTimelineObject * object, guint64 start)
{
GList *tmp, *tckobjs;
gboolean ret = TRUE;
GESTimeline *timeline;
g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
timeline = ges_timeline_layer_get_timeline (object->priv->layer);
if (timeline == NULL) {
GST_DEBUG ("Not in a timeline yet");
return FALSE;
}
tckobjs = ges_timeline_object_get_track_objects (object);
for (tmp = tckobjs; tmp; tmp = g_list_next (tmp)) {
if (ges_track_object_is_locked (tmp->data)) {
ret = timeline_trim_object (timeline, GES_TRACK_OBJECT (tmp->data),
NULL, GES_EDGE_START, start);
break;
}
}
g_list_free_full (tckobjs, g_object_unref);
return ret;
}
static void
update_height (GESTimelineObject * object)
{

View file

@ -182,6 +182,9 @@ struct _GESTimelineObject {
* #GESTrack.
* @fill_track_object: method to fill an associated #GESTrackObject.
* @need_fill_track: Set to TRUE if @fill_track_object needs to be called.
* @snaps: Set to %TRUE if the objects of this type snap with
* other objects in a timeline %FALSE otherwise (default is %FALSE). Basically only
* sources snap.
* @track_object_added: Should be overridden by subclasses if they need to perform an
* operation when a #GESTrackObject is added. Since: 0.10.2
* @track_object_released: Should be overridden by subclasses if they need to perform
@ -202,6 +205,7 @@ struct _GESTimelineObjectClass {
/* FIXME : might need a release_track_object */
GESFillTrackObjectFunc fill_track_object;
gboolean need_fill_track;
gboolean snaps;
void (*track_object_added) (GESTimelineObject *object,
GESTrackObject *tck_object);
@ -212,7 +216,7 @@ struct _GESTimelineObjectClass {
/*< private >*/
/* Padding for API extension */
gpointer _ges_reserved[GES_PADDING - 3];
gpointer _ges_reserved[GES_PADDING - 4];
};
GType ges_timeline_object_get_type (void);
@ -298,10 +302,31 @@ ges_timeline_object_set_supported_formats (GESTimelineObject * object,
GESTrackType supportedformats);
GESTimelineObject *
ges_timeline_object_split (GESTimelineObject * object, gint64 position);
ges_timeline_object_split (GESTimelineObject * object, guint64 position);
gboolean
ges_timeline_object_edit (GESTimelineObject * object,
GList *layers, gint new_layer_priority,
GESEditMode mode, GESEdge edge,
guint64 position);
void
ges_timeline_object_objects_set_locked (GESTimelineObject * object, gboolean locked);
gboolean ges_timeline_object_ripple (GESTimelineObject *object,
guint64 start);
gboolean ges_timeline_object_ripple_end (GESTimelineObject *object,
guint64 end);
gboolean ges_timeline_object_roll_start (GESTimelineObject *object,
guint64 start);
gboolean ges_timeline_object_roll_end (GESTimelineObject *object,
guint64 end);
gboolean ges_timeline_object_trim_start (GESTimelineObject *object,
guint64 start);
G_END_DECLS
#endif /* _GES_TIMELINE_OBJECT */

View file

@ -72,6 +72,8 @@ static OutputChain *get_output_chain_for_track (GESTimelinePipeline * self,
GESTrack * track);
static OutputChain *new_output_chain_for_track (GESTimelinePipeline * self,
GESTrack * track);
static gboolean play_sink_multiple_seeks_send_event (GstElement * element,
GstEvent * event);
static void
ges_timeline_pipeline_dispose (GObject * object)
@ -122,6 +124,8 @@ ges_timeline_pipeline_class_init (GESTimelinePipelineClass * klass)
static void
ges_timeline_pipeline_init (GESTimelinePipeline * self)
{
GstElementClass *playsinkclass;
GST_INFO_OBJECT (self, "Creating new 'playsink'");
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
GES_TYPE_TIMELINE_PIPELINE, GESTimelinePipelinePrivate);
@ -141,6 +145,12 @@ ges_timeline_pipeline_init (GESTimelinePipeline * self)
if (G_UNLIKELY (self->priv->encodebin == NULL))
goto no_encodebin;
/* TODO : Remove this hack once we depend on gst-p-base 0.10.37 */
/* HACK : Intercept events going through playsink */
playsinkclass = GST_ELEMENT_GET_CLASS (self->priv->playsink);
/* Replace playsink's GstBin::send_event with our own */
playsinkclass->send_event = play_sink_multiple_seeks_send_event;
ges_timeline_pipeline_set_mode (self, DEFAULT_TIMELINE_MODE);
return;
@ -997,3 +1007,16 @@ ges_timeline_pipeline_preview_set_audio_sink (GESTimelinePipeline * self,
{
g_object_set (self->priv->playsink, "audio-sink", sink, NULL);
};
static gboolean
play_sink_multiple_seeks_send_event (GstElement * element, GstEvent * event)
{
GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
GST_DEBUG ("%s", GST_EVENT_TYPE_NAME (event));
return
GST_ELEMENT_CLASS (g_type_class_peek_parent (klass))->send_event (element,
event);
}

View file

@ -79,6 +79,9 @@ ges_timeline_source_class_init (GESTimelineSourceClass * klass)
object_class->get_property = ges_timeline_source_get_property;
object_class->set_property = ges_timeline_source_set_property;
object_class->finalize = ges_timeline_source_finalize;
/* All subclasses should have snapping enabled */
GES_TIMELINE_OBJECT_CLASS (klass)->snaps = TRUE;
}
static void

File diff suppressed because it is too large Load diff

View file

@ -308,8 +308,8 @@ ges_track_object_class_init (GESTrackObjectClass * klass)
*/
g_object_class_install_property (object_class, PROP_MAX_DURATION,
g_param_spec_uint64 ("max-duration", "Maximum duration",
"The duration of the object", 0, G_MAXUINT64, G_MAXUINT64,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
"The duration of the object", GST_CLOCK_TIME_NONE, G_MAXUINT64,
G_MAXUINT64, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
/**
* GESTrackObject::deep-notify:
@ -337,17 +337,18 @@ ges_track_object_class_init (GESTrackObjectClass * klass)
static void
ges_track_object_init (GESTrackObject * self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
GESTrackObjectPrivate *priv = self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
GES_TYPE_TRACK_OBJECT, GESTrackObjectPrivate);
/* Sane default values */
self->priv->pending_start = 0;
self->priv->pending_inpoint = 0;
self->priv->pending_duration = GST_SECOND;
self->priv->pending_priority = 1;
self->priv->pending_active = TRUE;
self->priv->locked = TRUE;
self->priv->properties_hashtable = NULL;
priv->pending_start = 0;
priv->pending_inpoint = 0;
priv->pending_duration = GST_SECOND;
priv->pending_priority = 1;
priv->pending_active = TRUE;
priv->locked = TRUE;
priv->properties_hashtable = NULL;
priv->maxduration = GST_CLOCK_TIME_NONE;
}
static inline gboolean
@ -376,6 +377,8 @@ ges_track_object_set_start_internal (GESTrackObject * object, guint64 start)
void
ges_track_object_set_start (GESTrackObject * object, guint64 start)
{
g_return_if_fail (GES_IS_TRACK_OBJECT (object));
if (ges_track_object_set_start_internal (object, start))
#if GLIB_CHECK_VERSION(2,26,0)
g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_START]);
@ -412,6 +415,8 @@ ges_track_object_set_inpoint_internal (GESTrackObject * object, guint64 inpoint)
void
ges_track_object_set_inpoint (GESTrackObject * object, guint64 inpoint)
{
g_return_if_fail (GES_IS_TRACK_OBJECT (object));
if (ges_track_object_set_inpoint_internal (object, inpoint))
#if GLIB_CHECK_VERSION(2,26,0)
g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_INPOINT]);
@ -424,17 +429,24 @@ static inline gboolean
ges_track_object_set_duration_internal (GESTrackObject * object,
guint64 duration)
{
GESTrackObjectPrivate *priv = object->priv;
GST_DEBUG ("object:%p, duration:%" GST_TIME_FORMAT,
object, GST_TIME_ARGS (duration));
if (object->priv->gnlobject != NULL) {
if (GST_CLOCK_TIME_IS_VALID (priv->maxduration) &&
duration > object->inpoint + priv->maxduration)
duration = priv->maxduration - object->inpoint;
if (priv->gnlobject != NULL) {
if (G_UNLIKELY (duration == object->duration))
return FALSE;
g_object_set (object->priv->gnlobject, "duration", duration,
g_object_set (priv->gnlobject, "duration", duration,
"media-duration", duration, NULL);
} else
object->priv->pending_duration = duration;
priv->pending_duration = duration;
return TRUE;
}
@ -449,6 +461,8 @@ ges_track_object_set_duration_internal (GESTrackObject * object,
void
ges_track_object_set_duration (GESTrackObject * object, guint64 duration)
{
g_return_if_fail (GES_IS_TRACK_OBJECT (object));
if (ges_track_object_set_duration_internal (object, duration))
#if GLIB_CHECK_VERSION(2,26,0)
g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_DURATION]);
@ -511,6 +525,8 @@ ges_track_object_set_priority (GESTrackObject * object, guint32 priority)
gboolean
ges_track_object_set_active (GESTrackObject * object, gboolean active)
{
g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), FALSE);
GST_DEBUG ("object:%p, active:%d", object, active);
if (object->priv->gnlobject != NULL) {
@ -631,7 +647,7 @@ gnlobject_duration_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
g_object_get (gnlobject, "duration", &duration, NULL);
GST_DEBUG ("gnlobject duration : %" GST_TIME_FORMAT " current : %"
GST_DEBUG_OBJECT (gnlobject, "duration : %" GST_TIME_FORMAT " current : %"
GST_TIME_FORMAT, GST_TIME_ARGS (duration), GST_TIME_ARGS (obj->duration));
if (duration != obj->duration) {
@ -765,20 +781,21 @@ ensure_gnl_object (GESTrackObject * object)
GST_DEBUG ("Calling virtual method");
/* call the create_gnl_object virtual method */
gnlobject = class->create_gnl_object (object);
if (G_UNLIKELY (gnlobject == NULL)) {
GST_ERROR
("'create_gnl_object' implementation returned TRUE but no GnlObject is available");
goto done;
}
object->priv->gnlobject = gnlobject;
/* 2. Fill in the GnlObject */
if (gnlobject) {
GST_DEBUG ("Got a valid GnlObject, now filling it in");
if (object->priv->gnlobject == NULL) {
/* call the create_gnl_object virtual method */
gnlobject = class->create_gnl_object (object);
if (G_UNLIKELY (gnlobject == NULL)) {
GST_ERROR
("'create_gnl_object' implementation returned TRUE but no GnlObject is available");
goto done;
}
GST_DEBUG_OBJECT (object, "Got a valid GnlObject, now filling it in");
object->priv->gnlobject = gnlobject;
if (object->priv->timelineobj)
res = ges_timeline_object_fill_track_object (object->priv->timelineobj,
@ -802,7 +819,6 @@ ensure_gnl_object (GESTrackObject * object)
/* Set some properties on the GnlObject */
g_object_set (object->priv->gnlobject,
"caps", ges_track_get_caps (object->priv->track),
"duration", object->priv->pending_duration,
"media-duration", object->priv->pending_duration,
"start", object->priv->pending_start,
@ -810,6 +826,10 @@ ensure_gnl_object (GESTrackObject * object)
"priority", object->priv->pending_priority,
"active", object->priv->pending_active, NULL);
if (object->priv->track != NULL)
g_object_set (object->priv->gnlobject,
"caps", ges_track_get_caps (object->priv->track), NULL);
/* We feed up the props_hashtable if possible */
if (class->get_props_hastable) {
props_hash = class->get_props_hastable (object);
@ -841,8 +861,16 @@ ges_track_object_set_track (GESTrackObject * object, GESTrack * track)
object->priv->track = track;
if (object->priv->track)
return ensure_gnl_object (object);
if (object->priv->track) {
/* If we already have a gnlobject, we just set its caps properly */
if (object->priv->gnlobject) {
g_object_set (object->priv->gnlobject,
"caps", ges_track_get_caps (object->priv->track), NULL);
return TRUE;
} else {
return ensure_gnl_object (object);
}
}
return TRUE;
}
@ -969,6 +997,8 @@ ges_track_object_set_locked (GESTrackObject * object, gboolean locked)
gboolean
ges_track_object_is_locked (GESTrackObject * object)
{
g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), FALSE);
return object->priv->locked;
}
@ -978,13 +1008,16 @@ ges_track_object_is_locked (GESTrackObject * object)
*
* Get the position of the object in the container #GESTrack.
*
* Returns: the start position (in #GstClockTime)
* Returns: the start position (in #GstClockTime) or #GST_CLOCK_TIME_NONE
* if something went wrong.
*
* Since: 0.10.2
*/
guint64
ges_track_object_get_start (GESTrackObject * object)
{
g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), GST_CLOCK_TIME_NONE);
if (G_UNLIKELY (object->priv->gnlobject == NULL))
return object->priv->pending_start;
else
@ -997,13 +1030,16 @@ ges_track_object_get_start (GESTrackObject * object)
*
* Get the offset within the contents of this #GESTrackObject
*
* Returns: the in-point (in #GstClockTime)
* Returns: the in-point (in #GstClockTime) or #GST_CLOCK_TIME_NONE
* if something went wrong.
*
* Since: 0.10.2
*/
guint64
ges_track_object_get_inpoint (GESTrackObject * object)
{
g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), GST_CLOCK_TIME_NONE);
if (G_UNLIKELY (object->priv->gnlobject == NULL))
return object->priv->pending_inpoint;
else
@ -1017,13 +1053,16 @@ ges_track_object_get_inpoint (GESTrackObject * object)
* Get the duration which will be used in the container #GESTrack
* starting from the 'in-point'
*
* Returns: the duration (in #GstClockTime)
* Returns: the duration (in #GstClockTime) or #GST_CLOCK_TIME_NONE
* if something went wrong.
*
* Since: 0.10.2
*/
guint64
ges_track_object_get_duration (GESTrackObject * object)
{
g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), GST_CLOCK_TIME_NONE);
if (G_UNLIKELY (object->priv->gnlobject == NULL))
return object->priv->pending_duration;
else
@ -1036,13 +1075,15 @@ ges_track_object_get_duration (GESTrackObject * object)
*
* Get the priority of the object withing the containing #GESTrack.
*
* Returns: the priority of @object
* Returns: the priority of @object or -1 if something went wrong
*
* Since: 0.10.2
*/
guint32
ges_track_object_get_priority (GESTrackObject * object)
{
g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), -1);
if (G_UNLIKELY (object->priv->gnlobject == NULL))
return object->priv->pending_priority;
else
@ -1063,6 +1104,8 @@ ges_track_object_get_priority (GESTrackObject * object)
gboolean
ges_track_object_is_active (GESTrackObject * object)
{
g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), FALSE);
if (G_UNLIKELY (object->priv->gnlobject == NULL))
return object->priv->pending_active;
else
@ -1100,7 +1143,11 @@ ges_track_object_lookup_child (GESTrackObject * object, const gchar * prop_name,
gpointer key, value;
gchar **names, *name, *classename;
gboolean res;
GESTrackObjectPrivate *priv = object->priv;
GESTrackObjectPrivate *priv;
g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), FALSE);
priv = object->priv;
classename = NULL;
res = FALSE;
@ -1147,8 +1194,11 @@ ges_track_object_set_child_property_by_pspec (GESTrackObject * object,
GParamSpec * pspec, GValue * value)
{
GstElement *element;
GESTrackObjectPrivate *priv;
GESTrackObjectPrivate *priv = object->priv;
g_return_if_fail (GES_IS_TRACK_OBJECT (object));
priv = object->priv;
if (!priv->properties_hashtable)
goto prop_hash_not_set;
@ -1198,7 +1248,7 @@ ges_track_object_set_child_property_valist (GESTrackObject * object,
gchar *error = NULL;
GValue value = { 0, };
g_return_if_fail (G_IS_OBJECT (object));
g_return_if_fail (GES_IS_TRACK_OBJECT (object));
name = first_property_name;
@ -1264,6 +1314,8 @@ ges_track_object_set_child_property (GESTrackObject * object,
{
va_list var_args;
g_return_if_fail (GES_IS_TRACK_OBJECT (object));
va_start (var_args, first_property_name);
ges_track_object_set_child_property_valist (object, first_property_name,
var_args);
@ -1372,6 +1424,8 @@ ges_track_object_get_child_property (GESTrackObject * object,
{
va_list var_args;
g_return_if_fail (GES_IS_TRACK_OBJECT (object));
va_start (var_args, first_property_name);
ges_track_object_get_child_property_valist (object, first_property_name,
var_args);
@ -1393,8 +1447,11 @@ ges_track_object_get_child_property_by_pspec (GESTrackObject * object,
GParamSpec * pspec, GValue * value)
{
GstElement *element;
GESTrackObjectPrivate *priv;
GESTrackObjectPrivate *priv = object->priv;
g_return_if_fail (GES_IS_TRACK_OBJECT (object));
priv = object->priv;
if (!priv->properties_hashtable)
goto prop_hash_not_set;
@ -1485,3 +1542,134 @@ ges_track_object_set_max_duration (GESTrackObject * object, guint64 maxduration)
object->priv->maxduration = maxduration;
}
/**
* ges_track_object_copy:
* @object: The #GESTrackObject to copy
* @deep: whether we want to create the gnlobject and copy it properties
*
* Copies @object
*
* Returns: The newly create #GESTrackObject, copied from @object
*
* Since: 0.10.XX
*/
GESTrackObject *
ges_track_object_copy (GESTrackObject * object, gboolean deep)
{
GESTrackObject *ret = NULL;
GParameter *params;
GParamSpec **specs;
guint n, n_specs, n_params;
GValue val = { 0 };
g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), NULL);
specs =
g_object_class_list_properties (G_OBJECT_GET_CLASS (object), &n_specs);
params = g_new0 (GParameter, n_specs);
n_params = 0;
for (n = 0; n < n_specs; ++n) {
if (g_strcmp0 (specs[n]->name, "parent") &&
(specs[n]->flags & G_PARAM_READWRITE) == G_PARAM_READWRITE) {
params[n_params].name = g_intern_string (specs[n]->name);
g_value_init (&params[n_params].value, specs[n]->value_type);
g_object_get_property (G_OBJECT (object), specs[n]->name,
&params[n_params].value);
++n_params;
}
}
ret = g_object_newv (G_TYPE_FROM_INSTANCE (object), n_params, params);
g_free (specs);
g_free (params);
specs = NULL;
params = NULL;
if (deep == FALSE)
return ret;
ensure_gnl_object (ret);
specs = ges_track_object_list_children_properties (object, &n_specs);
for (n = 0; n < n_specs; ++n) {
g_value_init (&val, specs[n]->value_type);
g_object_get_property (G_OBJECT (object), specs[n]->name, &val);
ges_track_object_set_child_property_by_pspec (ret, specs[n], &val);
g_value_unset (&val);
}
g_free (specs);
g_free (params);
return ret;
}
/**
* ges_track_object_edit:
* @object: the #GESTrackObject to edit
* @layers: (element-type GESTimelineLayer): The layers you want the edit to
* happen in, %NULL means that the edition is done in all the
* #GESTimelineLayers contained in the current timeline.
* FIXME: This is not implemented yet.
* @mode: The #GESEditMode in which the editition will happen.
* @edge: The #GESEdge the edit should happen on.
* @position: The position at which to edit @object (in nanosecond)
*
* Edit @object in the different exisiting #GESEditMode modes. In the case of
* slide, and roll, you need to specify a #GESEdge
*
* Returns: %TRUE if the object as been edited properly, %FALSE if an error
* occured
*
* Since: 0.10.XX
*/
gboolean
ges_track_object_edit (GESTrackObject * object,
GList * layers, GESEditMode mode, GESEdge edge, guint64 position)
{
GESTrack *track = ges_track_object_get_track (object);
GESTimeline *timeline;
g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), FALSE);
if (G_UNLIKELY (!track)) {
GST_WARNING_OBJECT (object, "Trying to edit in %d mode but not in"
"any Track yet.", mode);
return FALSE;
} else if (position < 0) {
GST_DEBUG_OBJECT (object, "Trying to move before 0, not moving");
return FALSE;
}
timeline = GES_TIMELINE (ges_track_get_timeline (track));
if (G_UNLIKELY (!timeline)) {
GST_WARNING_OBJECT (object, "Trying to edit in %d mode but not in"
"track %p no in any timeline yet.", mode, track);
return FALSE;
}
switch (mode) {
case GES_EDIT_MODE_NORMAL:
timeline_move_object (timeline, object, layers, edge, position);
break;
case GES_EDIT_MODE_TRIM:
timeline_trim_object (timeline, object, layers, edge, position);
break;
case GES_EDIT_MODE_RIPPLE:
timeline_ripple_object (timeline, object, layers, edge, position);
break;
case GES_EDIT_MODE_ROLL:
timeline_roll_object (timeline, object, layers, edge, position);
break;
case GES_EDIT_MODE_SLIDE:
timeline_slide_object (timeline, object, layers, edge, position);
break;
default:
GST_ERROR ("Unkown edit mode: %d", mode);
return FALSE;
}
return TRUE;
}

View file

@ -226,6 +226,13 @@ void ges_track_object_set_child_property (GESTrackObject * object,
const gchar * first_property_name,
...) G_GNUC_NULL_TERMINATED;
GESTrackObject * ges_track_object_copy (GESTrackObject * object,
gboolean deep);
gboolean
ges_track_object_edit (GESTrackObject * object,
GList *layers, GESEditMode mode,
GESEdge edge, guint64 position);
G_END_DECLS
#endif /* _GES_TRACK_OBJECT */

View file

@ -455,14 +455,6 @@ ges_track_add_object (GESTrack * track, GESTrackObject * object)
return FALSE;
}
/* At this point, the track object shouldn't have any gnlobject since
* it hasn't been added to a track yet.
* FIXME : This check seems a bit obsolete */
if (G_UNLIKELY (ges_track_object_get_gnlobject (object) != NULL)) {
GST_ERROR ("TrackObject already controls a gnlobject !");
return FALSE;
}
if (G_UNLIKELY (!ges_track_object_set_track (object, track))) {
GST_ERROR ("Couldn't properly add the object to the Track");
return FALSE;

View file

@ -22,6 +22,7 @@ check_PROGRAMS = \
ges/filesource \
ges/simplelayer \
ges/timelineobject \
ges/timelineedition \
ges/titles\
ges/transition \
ges/overlays\

View file

@ -115,19 +115,21 @@ GST_START_TEST (test_ges_scenario)
ges_timeline_object_get_track_objects (GES_TIMELINE_OBJECT (source));
fail_unless (trackobjects != NULL);
trackobject = GES_TRACK_OBJECT (trackobjects->data);
/* There are 3 references:
/* There are 4 references:
* 1 by the timelineobject
* 1 by the track
* 1 by the timeline
* 1 added by the call to _get_track_objects() above */
ASSERT_OBJECT_REFCOUNT (trackobject, "trackobject", 3);
ASSERT_OBJECT_REFCOUNT (trackobject, "trackobject", 4);
for (tmp = trackobjects; tmp; tmp = tmp->next) {
g_object_unref (GES_TRACK_OBJECT (tmp->data));
}
g_list_free (trackobjects);
/* There are 2 references:
/* There are 3 references:
* 1 by the timelineobject
* 1 by the timeline
* 1 by the track */
ASSERT_OBJECT_REFCOUNT (trackobject, "trackobject", 2);
ASSERT_OBJECT_REFCOUNT (trackobject, "trackobject", 3);
GST_DEBUG ("Remove the TimelineObject from the layer");
@ -252,19 +254,21 @@ GST_START_TEST (test_ges_timeline_add_layer)
ges_timeline_object_get_track_objects (GES_TIMELINE_OBJECT (s1));
fail_unless (trackobjects != NULL);
trackobject = GES_TRACK_OBJECT (trackobjects->data);
/* There are 3 references:
/* There are 4 references:
* 1 by the timelineobject
* 1 by the trackobject
* 1 by the timeline
* 1 added by the call to _get_track_objects() above */
ASSERT_OBJECT_REFCOUNT (trackobject, "trackobject", 3);
ASSERT_OBJECT_REFCOUNT (trackobject, "trackobject", 4);
for (tmp = trackobjects; tmp; tmp = tmp->next) {
g_object_unref (GES_TRACK_OBJECT (tmp->data));
}
g_list_free (trackobjects);
/* There are 2 references:
/* There are 3 references:
* 1 by the timelineobject
* 1 by the timeline
* 1 by the trackobject */
ASSERT_OBJECT_REFCOUNT (trackobject, "trackobject", 2);
ASSERT_OBJECT_REFCOUNT (trackobject, "trackobject", 3);
trackobjects =
ges_timeline_object_get_track_objects (GES_TIMELINE_OBJECT (s2));
@ -274,10 +278,11 @@ GST_START_TEST (test_ges_timeline_add_layer)
g_object_unref (GES_TRACK_OBJECT (tmp->data));
}
g_list_free (trackobjects);
/* There are 2 references:
/* There are 3 references:
* 1 by the timelineobject
* 1 by the timeline
* 1 by the trackobject */
ASSERT_OBJECT_REFCOUNT (GES_TRACK_OBJECT (trackobject), "trackobject", 2);
ASSERT_OBJECT_REFCOUNT (GES_TRACK_OBJECT (trackobject), "trackobject", 3);
trackobjects =
ges_timeline_object_get_track_objects (GES_TIMELINE_OBJECT (s3));
@ -287,10 +292,11 @@ GST_START_TEST (test_ges_timeline_add_layer)
g_object_unref (GES_TRACK_OBJECT (tmp->data));
}
g_list_free (trackobjects);
/* There are 2 references:
/* There are 3 references:
* 1 by the timelineobject
* 1 by the timeline
* 1 by the trackobject */
ASSERT_OBJECT_REFCOUNT (trackobject, "trackobject", 2);
ASSERT_OBJECT_REFCOUNT (trackobject, "trackobject", 3);
/* theoretically this is all we need to do to ensure cleanup */
g_object_unref (timeline);
@ -369,11 +375,12 @@ GST_START_TEST (test_ges_timeline_add_layer_first)
ges_timeline_object_get_track_objects (GES_TIMELINE_OBJECT (s1));
fail_unless (trackobjects != NULL);
for (tmp = trackobjects; tmp; tmp = tmp->next) {
/* Each object has 3 references:
/* Each object has 4 references:
* 1 by the timelineobject
* 1 by the track
* 1 by the timeline
* 1 added by _get_track_object() above */
ASSERT_OBJECT_REFCOUNT (GES_TRACK_OBJECT (tmp->data), "trackobject", 3);
ASSERT_OBJECT_REFCOUNT (GES_TRACK_OBJECT (tmp->data), "trackobject", 4);
g_object_unref (GES_TRACK_OBJECT (tmp->data));
}
g_list_free (trackobjects);
@ -382,11 +389,12 @@ GST_START_TEST (test_ges_timeline_add_layer_first)
ges_timeline_object_get_track_objects (GES_TIMELINE_OBJECT (s2));
fail_unless (trackobjects != NULL);
for (tmp = trackobjects; tmp; tmp = tmp->next) {
/* Each object has 3 references:
/* Each object has 4 references:
* 1 by the timelineobject
* 1 by the track
* 1 by the timeline
* 1 added by _get_track_object() above */
ASSERT_OBJECT_REFCOUNT (GES_TRACK_OBJECT (tmp->data), "trackobject", 3);
ASSERT_OBJECT_REFCOUNT (GES_TRACK_OBJECT (tmp->data), "trackobject", 4);
g_object_unref (GES_TRACK_OBJECT (tmp->data));
}
g_list_free (trackobjects);
@ -395,11 +403,12 @@ GST_START_TEST (test_ges_timeline_add_layer_first)
ges_timeline_object_get_track_objects (GES_TIMELINE_OBJECT (s3));
fail_unless (trackobjects != NULL);
for (tmp = trackobjects; tmp; tmp = tmp->next) {
/* Each object has 3 references:
/* Each object has 4 references:
* 1 by the timelineobject
* 1 by the track
* 1 by the timeline
* 1 added by _get_track_object() above */
ASSERT_OBJECT_REFCOUNT (GES_TRACK_OBJECT (tmp->data), "trackobject", 3);
ASSERT_OBJECT_REFCOUNT (GES_TRACK_OBJECT (tmp->data), "trackobject", 4);
g_object_unref (GES_TRACK_OBJECT (tmp->data));
}
g_list_free (trackobjects);
@ -482,67 +491,73 @@ GST_START_TEST (test_ges_timeline_remove_track)
fail_unless (trackobjects != NULL);
t1 = GES_TRACK_OBJECT ((trackobjects)->data);
for (tmp = trackobjects; tmp; tmp = tmp->next) {
/* There are 3 references held:
/* There are 4 references held:
* 1 by the timelineobject
* 1 by the track
* 1 by the timeline
* 1 added by the call to _get_track_objects() above */
ASSERT_OBJECT_REFCOUNT (GES_TRACK_OBJECT (tmp->data), "trackobject", 3);
ASSERT_OBJECT_REFCOUNT (GES_TRACK_OBJECT (tmp->data), "trackobject", 4);
g_object_unref (GES_TRACK_OBJECT (tmp->data));
}
g_object_ref (t1);
g_list_free (trackobjects);
/* There are 3 references held:
/* There are 4 references held:
* 1 by the timelinobject
* 1 by the track
* 1 by the timeline
* 1 added by ourselves above (g_object_ref (t1)) */
ASSERT_OBJECT_REFCOUNT (t1, "trackobject", 3);
ASSERT_OBJECT_REFCOUNT (t1, "trackobject", 4);
trackobjects =
ges_timeline_object_get_track_objects (GES_TIMELINE_OBJECT (s2));
fail_unless (trackobjects != NULL);
t2 = GES_TRACK_OBJECT (trackobjects->data);
for (tmp = trackobjects; tmp; tmp = tmp->next) {
/* There are 3 references held:
/* There are 4 references held:
* 1 by the timelineobject
* 1 by the track
* 1 by the timeline
* 1 added by the call to _get_track_objects() above */
ASSERT_OBJECT_REFCOUNT (GES_TRACK_OBJECT (tmp->data), "trackobject", 3);
ASSERT_OBJECT_REFCOUNT (GES_TRACK_OBJECT (tmp->data), "trackobject", 4);
g_object_unref (GES_TRACK_OBJECT (tmp->data));
}
g_object_ref (t2);
g_list_free (trackobjects);
/* There are 3 references held:
/* There are 4 references held:
* 1 by the timelinobject
* 1 by the track
* 1 by the timeline
* 1 added by ourselves above (g_object_ref (t1)) */
ASSERT_OBJECT_REFCOUNT (t2, "t2", 3);
ASSERT_OBJECT_REFCOUNT (t2, "t2", 4);
trackobjects =
ges_timeline_object_get_track_objects (GES_TIMELINE_OBJECT (s3));
fail_unless (trackobjects != NULL);
t3 = GES_TRACK_OBJECT (trackobjects->data);
for (tmp = trackobjects; tmp; tmp = tmp->next) {
/* There are 3 references held:
/* There are 4 references held:
* 1 by the timelineobject
* 1 by the track
* 1 by the timeline
* 1 added by the call to _get_track_objects() above */
ASSERT_OBJECT_REFCOUNT (GES_TRACK_OBJECT (tmp->data), "trackobject", 3);
ASSERT_OBJECT_REFCOUNT (GES_TRACK_OBJECT (tmp->data), "trackobject", 4);
g_object_unref (GES_TRACK_OBJECT (tmp->data));
}
g_object_ref (t3);
g_list_free (trackobjects);
/* There are 3 references held:
/* There are 4 references held:
* 1 by the timelinobject
* 1 by the track
* 1 by the timeline
* 1 added by ourselves above (g_object_ref (t1)) */
ASSERT_OBJECT_REFCOUNT (t3, "t3", 3);
ASSERT_OBJECT_REFCOUNT (t3, "t3", 4);
/* remove the track and check that the track objects have been released */
fail_unless (ges_timeline_remove_track (timeline, track));
ASSERT_OBJECT_REFCOUNT (t1, "trackobject", 1);
ASSERT_OBJECT_REFCOUNT (t2, "trackobject", 1);
ASSERT_OBJECT_REFCOUNT (t3, "trackobject", 1);
ASSERT_OBJECT_REFCOUNT (t1, "trackobject", 2);
ASSERT_OBJECT_REFCOUNT (t2, "trackobject", 2);
ASSERT_OBJECT_REFCOUNT (t3, "trackobject", 2);
g_object_unref (t1);
g_object_unref (t2);

View file

@ -0,0 +1,975 @@
/* GStreamer Editing Services
*
* Copyright (C) <2012> 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.
*/
#include <ges/ges.h>
#include <gst/check/gstcheck.h>
static gboolean
my_fill_track_func (GESTimelineObject * object,
GESTrackObject * trobject, GstElement * gnlobj, gpointer user_data)
{
GstElement *src;
GST_DEBUG ("timelineobj:%p, trackobjec:%p, gnlobj:%p",
object, trobject, gnlobj);
/* Let's just put a fakesource in for the time being */
src = gst_element_factory_make ("fakesrc", NULL);
/* If this fails... that means that there already was something
* in it */
fail_unless (gst_bin_add (GST_BIN (gnlobj), src));
return TRUE;
}
static inline GESTimelineObject *
create_custom_tlobj (void)
{
return
GES_TIMELINE_OBJECT (ges_custom_timeline_source_new (my_fill_track_func,
NULL));
}
#define CHECK_OBJECT_PROPS(obj, start, inpoint, duration) {\
assert_equals_uint64 (ges_track_object_get_start (obj), start);\
assert_equals_uint64 (ges_track_object_get_inpoint (obj), inpoint);\
assert_equals_uint64 (ges_track_object_get_duration (obj), duration);\
}
GST_START_TEST (test_basic_timeline_edition)
{
GESTrack *track;
GESTimeline *timeline;
GESTrackObject *tckobj, *tckobj1, *tckobj2;
GESTimelineObject *obj, *obj1, *obj2;
ges_init ();
track = ges_track_new (GES_TRACK_TYPE_CUSTOM, GST_CAPS_ANY);
fail_unless (track != NULL);
timeline = ges_timeline_new ();
fail_unless (timeline != NULL);
fail_unless (ges_timeline_add_track (timeline, track));
obj = create_custom_tlobj ();
obj1 = create_custom_tlobj ();
obj2 = create_custom_tlobj ();
fail_unless (obj && obj1 && obj2);
/**
* Our timeline
*
* inpoints 0------- 0-------- 0-----------
* | obj | | obj1 | | obj2 |
* time 0------- 10 --------20 50---------60
*/
g_object_set (obj, "start", (guint64) 0, "duration", (guint64) 10,
"in-point", (guint64) 0, NULL);
g_object_set (obj1, "start", (guint64) 10, "duration", (guint64) 10,
"in-point", (guint64) 0, NULL);
g_object_set (obj2, "start", (guint64) 50, "duration", (guint64) 60,
"in-point", (guint64) 0, NULL);
tckobj = ges_timeline_object_create_track_object (obj, track);
fail_unless (tckobj != NULL);
fail_unless (ges_timeline_object_add_track_object (obj, tckobj));
fail_unless (ges_track_add_object (track, tckobj));
assert_equals_uint64 (ges_track_object_get_duration (tckobj), 10);
tckobj1 = ges_timeline_object_create_track_object (obj1, track);
fail_unless (tckobj1 != NULL);
fail_unless (ges_timeline_object_add_track_object (obj1, tckobj1));
fail_unless (ges_track_add_object (track, tckobj1));
assert_equals_uint64 (ges_track_object_get_duration (tckobj1), 10);
tckobj2 = ges_timeline_object_create_track_object (obj2, track);
fail_unless (ges_timeline_object_add_track_object (obj2, tckobj2));
fail_unless (tckobj2 != NULL);
fail_unless (ges_track_add_object (track, tckobj2));
assert_equals_uint64 (ges_track_object_get_duration (tckobj2), 60);
/**
* Simple rippling obj to: 10
*
* New timeline:
* ------------
*
* inpoints 0------- 0-------- 0-----------
* | obj | | obj1 | | obj2 |
* time 10------- 20 --------30 60---------120
*/
fail_unless (ges_timeline_object_edit (obj, NULL, -1, GES_EDIT_MODE_RIPPLE,
GES_EDGE_NONE, 10) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 10, 0, 10);
CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
CHECK_OBJECT_PROPS (tckobj2, 60, 0, 60);
/* FIXME find a way to check that we are using the same MovingContext
* inside the GESTrack */
fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_RIPPLE,
GES_EDGE_NONE, 40) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 10, 0, 10);
CHECK_OBJECT_PROPS (tckobj1, 40, 0, 10);
CHECK_OBJECT_PROPS (tckobj2, 80, 0, 60);
/**
* Rippling obj1 back to: 20 (getting to the exact same timeline as before
*/
fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_RIPPLE,
GES_EDGE_NONE, 20) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 10, 0, 10);
CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
CHECK_OBJECT_PROPS (tckobj2, 60, 0, 60);
/**
* Simple move obj to: 27 and obj2 to 35
*
* New timeline:
* ------------
* 0------------
* inpoints 0-------|--- obj 0--|----------
* | obj1 27 -|-----|-37 obj2 |
* time 20-----------30 35-------------120
*/
fail_unless (ges_timeline_object_edit (obj, NULL, -1, GES_EDIT_MODE_NORMAL,
GES_EDGE_NONE, 27) == TRUE);
fail_unless (ges_timeline_object_edit (obj2, NULL, -1, GES_EDIT_MODE_NORMAL,
GES_EDGE_NONE, 35) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 27, 0, 10);
CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
CHECK_OBJECT_PROPS (tckobj2, 35, 0, 60);
/**
* Trim start obj to: 32 and obj2 to 35
*
* New timeline:
* ------------
* 5--------
* inpoints 0----------- | obj 0--|----------
* | obj1 | 32----|-37 obj2 |
* time 20-----------30 35-------------120
*/
fail_unless (ges_timeline_object_edit (obj, NULL, -1, GES_EDIT_MODE_TRIM,
GES_EDGE_START, 32) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 32, 5, 5);
CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
CHECK_OBJECT_PROPS (tckobj2, 35, 0, 60);
/* Ripple end obj to 42
* New timeline:
* ------------
* 5--------
* inpoints 0----------- | obj 0--|----------
* | obj1 | 32----|-42 obj2 |
* time 20-----------30 35-------------120
*/
fail_unless (ges_timeline_object_edit (obj, NULL, -1, GES_EDIT_MODE_RIPPLE,
GES_EDGE_END, 42) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 32, 5, 10);
CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
CHECK_OBJECT_PROPS (tckobj2, 35, 0, 60);
/**
* New timeline:
* ------------
* inpoints 0------- 5-------- 0-----------
* | obj1 | | obj1 || obj2 |
* time 20-------30 32--------52 ---------112
*/
fail_unless (ges_timeline_object_edit (obj2, NULL, -1, GES_EDIT_MODE_NORMAL,
GES_EDGE_NONE, 42) == TRUE);
fail_unless (ges_timeline_object_edit (obj, NULL, -1, GES_EDIT_MODE_RIPPLE,
GES_EDGE_END, 52) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 32, 5, 20);
CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
CHECK_OBJECT_PROPS (tckobj2, 52, 0, 60);
/**
* New timeline:
* ------------
* inpoints 0------- 5-------- 0------------
* | obj1 | | obj || obj2 |
* time 20-------40 42--------62 ---------122
*/
fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_RIPPLE,
GES_EDGE_END, 40) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 42, 5, 20);
CHECK_OBJECT_PROPS (tckobj1, 20, 0, 20);
CHECK_OBJECT_PROPS (tckobj2, 62, 0, 60);
/**
* New timeline:
* ------------
* inpoints 0------- 0-------- 0-----------
* | obj1 || obj || obj2 |
* time 20------ 25 ------ 62 ---------122
*/
fail_unless (ges_timeline_object_edit (obj, NULL, -1, GES_EDIT_MODE_TRIM,
GES_EDGE_START, 40) == TRUE);
fail_unless (ges_timeline_object_edit (obj, NULL, -1, GES_EDIT_MODE_ROLL,
GES_EDGE_START, 25) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
CHECK_OBJECT_PROPS (tckobj1, 20, 0, 5);
CHECK_OBJECT_PROPS (tckobj2, 62, 0, 60);
/* Make sure that not doing anything when not able to roll */
fail_unless (ges_timeline_object_edit (obj, NULL, -1, GES_EDIT_MODE_ROLL,
GES_EDGE_START, 65) == TRUE);
fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_ROLL,
GES_EDGE_END, 65) == TRUE, 0);
CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
CHECK_OBJECT_PROPS (tckobj1, 20, 0, 5);
CHECK_OBJECT_PROPS (tckobj2, 62, 0, 60);
g_object_unref (timeline);
g_object_unref (obj);
g_object_unref (obj1);
g_object_unref (obj2);
}
GST_END_TEST;
GST_START_TEST (test_snapping)
{
GESTrack *track;
GESTimeline *timeline;
GESTrackObject *tckobj, *tckobj1, *tckobj2;
GESTimelineObject *obj, *obj1, *obj2;
GESTimelineLayer *layer;
GList *tckobjs;
ges_init ();
track = ges_track_new (GES_TRACK_TYPE_CUSTOM, GST_CAPS_ANY);
fail_unless (track != NULL);
timeline = ges_timeline_new ();
fail_unless (timeline != NULL);
fail_unless (ges_timeline_add_track (timeline, track));
obj = create_custom_tlobj ();
obj1 = create_custom_tlobj ();
obj2 = create_custom_tlobj ();
fail_unless (obj && obj1 && obj2);
/**
* Our timeline
* ------------
* inpoints 0------- 0-------- 0-----------
* | obj1 || obj || obj2 |
* time 20------ 25 ------ 62 ---------122
*/
g_object_set (obj, "start", (guint64) 25, "duration", (guint64) 37,
"in-point", (guint64) 0, NULL);
g_object_set (obj1, "start", (guint64) 20, "duration", (guint64) 15,
"in-point", (guint64) 0, NULL);
g_object_set (obj2, "start", (guint64) 62, "duration", (guint64) 60,
"in-point", (guint64) 0, NULL);
fail_unless ((layer = ges_timeline_append_layer (timeline)) != NULL);
assert_equals_int (ges_timeline_layer_get_priority (layer), 0);
fail_unless (ges_timeline_layer_add_object (layer, obj));
fail_unless ((tckobjs = ges_timeline_object_get_track_objects (obj)) != NULL);
fail_unless ((tckobj = GES_TRACK_OBJECT (tckobjs->data)) != NULL);
fail_unless (ges_track_object_get_track (tckobj) == track);
assert_equals_uint64 (ges_track_object_get_duration (tckobj), 37);
g_list_free_full (tckobjs, g_object_unref);
/* We have 3 references to tckobj from:
* track + timeline + obj */
ASSERT_OBJECT_REFCOUNT (tckobj, "First tckobj", 3);
/* We have 1 ref to obj1:
* + layer */
ASSERT_OBJECT_REFCOUNT (obj, "First tlobj", 1);
fail_unless (ges_timeline_layer_add_object (layer, obj1));
fail_unless ((tckobjs =
ges_timeline_object_get_track_objects (obj1)) != NULL);
fail_unless ((tckobj1 = GES_TRACK_OBJECT (tckobjs->data)) != NULL);
fail_unless (ges_track_object_get_track (tckobj1) == track);
assert_equals_uint64 (ges_track_object_get_duration (tckobj1), 15);
g_list_free_full (tckobjs, g_object_unref);
/* Same ref logic */
ASSERT_OBJECT_REFCOUNT (tckobj1, "First tckobj", 3);
ASSERT_OBJECT_REFCOUNT (obj1, "First tlobj", 1);
fail_unless (ges_timeline_layer_add_object (layer, obj2));
fail_unless ((tckobjs =
ges_timeline_object_get_track_objects (obj2)) != NULL);
fail_unless ((tckobj2 = GES_TRACK_OBJECT (tckobjs->data)) != NULL);
fail_unless (ges_track_object_get_track (tckobj2) == track);
assert_equals_uint64 (ges_track_object_get_duration (tckobj2), 60);
g_list_free_full (tckobjs, g_object_unref);
/* Same ref logic */
ASSERT_OBJECT_REFCOUNT (tckobj2, "First tckobj", 3);
ASSERT_OBJECT_REFCOUNT (obj2, "First tlobj", 1);
/* Snaping to edge, so no move */
g_object_set (timeline, "snapping-distance", (guint64) 3, NULL);
fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_TRIM,
GES_EDGE_END, 27) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
CHECK_OBJECT_PROPS (tckobj1, 20, 0, 5);
CHECK_OBJECT_PROPS (tckobj2, 62, 0, 60);
/* Snaping to edge, so no move */
fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_TRIM,
GES_EDGE_END, 27) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
CHECK_OBJECT_PROPS (tckobj1, 20, 0, 5);
CHECK_OBJECT_PROPS (tckobj2, 62, 0, 60);
/**
* New timeline:
* ------------
* 0----------- 0-------------
* inpoints 0-------|-- obj || obj2 |
* | obj1 25-|------- 62 -----------122
* time 20----------30
*/
g_object_set (timeline, "snapping-distance", (guint64) 0, NULL);
ges_timeline_object_set_duration (obj1, 10);
CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
CHECK_OBJECT_PROPS (tckobj2, 62, 0, 60);
/**
* New timeline(the "layers" are just to help reading diagram, nothing else):
* ------------
* 0----------
* | obj |
* 25---------62
* inpoints 0----------------------- 10--------
* | obj1 || obj2 |
* time 20---------------------- 72 --------122
*/
/* Rolling involves only neighbour that are currently snapping */
fail_unless (ges_timeline_object_roll_end (obj1, 62));
fail_unless (ges_timeline_object_roll_end (obj1, 72) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
CHECK_OBJECT_PROPS (tckobj1, 20, 0, 52);
CHECK_OBJECT_PROPS (tckobj2, 72, 10, 50);
/**
* 0----------
* | obj |
* 25---------62
* inpoints 5--------------- 10--------
* | obj1 || obj2 |
* time 25------------- 72 --------122
*/
g_object_set (timeline, "snapping-distance", (guint64) 4, NULL);
fail_unless (ges_timeline_object_trim_start (obj1, 28) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
CHECK_OBJECT_PROPS (tckobj1, 25, 5, 47);
CHECK_OBJECT_PROPS (tckobj2, 72, 10, 50);
/**
* 0----------
* | obj |
* 25---------62
* inpoints 5---------- 0---------
* | obj1 || obj2 |
* time 25-------- 62 --------122
*/
fail_unless (ges_timeline_object_roll_start (obj2, 59) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
CHECK_OBJECT_PROPS (tckobj1, 25, 5, 37);
CHECK_OBJECT_PROPS (tckobj2, 62, 0, 60);
/**
* inpoints 0----------5---------- 0----------
* | obj || obj1 || obj2 |
* time 25---------62-------- 99 --------170
*/
fail_unless (ges_timeline_object_ripple (obj1, 58) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
CHECK_OBJECT_PROPS (tckobj1, 62, 5, 37);
CHECK_OBJECT_PROPS (tckobj2, 99, 0, 60);
/**
* inpoints 0----------5---------- 0----------
* | obj || obj1 | | obj2 |
* time 25---------62-------- 99 110--------170
*/
ges_timeline_object_set_start (obj2, 110);
CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
CHECK_OBJECT_PROPS (tckobj1, 62, 5, 37);
CHECK_OBJECT_PROPS (tckobj2, 110, 0, 60);
/**
* inpoints 0----------5 5 --------- 0----------
* | obj | | obj1 || obj2 |
* time 25---------62 73---------110--------170
*/
fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_NORMAL,
GES_EDGE_NONE, 72) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
CHECK_OBJECT_PROPS (tckobj1, 73, 5, 37);
CHECK_OBJECT_PROPS (tckobj2, 110, 0, 60);
/**
* inpoints 0----------5---------- 0----------
* | obj || obj1 | | obj2 |
* time 25---------62-------- 99 110--------170
*/
fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_NORMAL,
GES_EDGE_NONE, 58) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
CHECK_OBJECT_PROPS (tckobj1, 62, 5, 37);
CHECK_OBJECT_PROPS (tckobj2, 110, 0, 60);
/**
* inpoints 0----------5---------- 0----------
* | obj || obj1 || obj2 |
* time 25---------62--------110--------170
*/
g_object_set (obj1, "duration", 46, NULL);
CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
CHECK_OBJECT_PROPS (tckobj1, 62, 5, 48);
CHECK_OBJECT_PROPS (tckobj2, 110, 0, 60);
/**
* inpoints 5----------- 0--------- 0----------
* | obj1 || obj2 || obj |
* time 62---------110--------170--------207
*/
g_object_set (obj, "start", 168, NULL);
CHECK_OBJECT_PROPS (tckobj, 170, 0, 37);
CHECK_OBJECT_PROPS (tckobj1, 62, 5, 48);
CHECK_OBJECT_PROPS (tckobj2, 110, 0, 60);
/* Check we didn't lose/screwed any references */
ASSERT_OBJECT_REFCOUNT (tckobj, "First tckobj", 3);
ASSERT_OBJECT_REFCOUNT (tckobj1, "Second tckobj", 3);
ASSERT_OBJECT_REFCOUNT (tckobj2, "Third tckobj", 3);
ASSERT_OBJECT_REFCOUNT (obj, "First tlobj", 1);
ASSERT_OBJECT_REFCOUNT (obj1, "Second tlobj", 1);
ASSERT_OBJECT_REFCOUNT (obj2, "Third tlobj", 1);
g_object_unref (timeline);
/* Check we destroyed everything */
fail_if (G_IS_OBJECT (tckobj));
fail_if (G_IS_OBJECT (tckobj1));
fail_if (G_IS_OBJECT (tckobj2));
fail_if (G_IS_OBJECT (obj));
fail_if (G_IS_OBJECT (obj1));
fail_if (G_IS_OBJECT (obj2));
fail_if (G_IS_OBJECT (layer));
}
GST_END_TEST;
GST_START_TEST (test_timeline_edition_mode)
{
guint i;
GESTrack *track;
GESTimeline *timeline;
GESTrackObject *tckobj, *tckobj1, *tckobj2;
GESTimelineObject *obj, *obj1, *obj2;
GESTimelineLayer *layer, *layer1, *layer2;
GList *tckobjs, *layers, *tmp;
ges_init ();
track = ges_track_new (GES_TRACK_TYPE_CUSTOM, GST_CAPS_ANY);
fail_unless (track != NULL);
timeline = ges_timeline_new ();
fail_unless (timeline != NULL);
fail_unless (ges_timeline_add_track (timeline, track));
obj = create_custom_tlobj ();
obj1 = create_custom_tlobj ();
obj2 = create_custom_tlobj ();
fail_unless (obj && obj1 && obj2);
/**
* Our timeline
*
* 0-------
* layer: | obj |
* 0-------10
*
* 0-------- 0-----------
* layer1: | obj1 | | obj2 |
* 10--------20 50---------60
*/
g_object_set (obj, "start", (guint64) 0, "duration", (guint64) 10,
"in-point", (guint64) 0, NULL);
g_object_set (obj1, "start", (guint64) 10, "duration", (guint64) 10,
"in-point", (guint64) 0, NULL);
g_object_set (obj2, "start", (guint64) 50, "duration", (guint64) 60,
"in-point", (guint64) 0, NULL);
fail_unless ((layer = ges_timeline_append_layer (timeline)) != NULL);
assert_equals_int (ges_timeline_layer_get_priority (layer), 0);
fail_unless (ges_timeline_layer_add_object (layer, obj));
fail_unless ((tckobjs = ges_timeline_object_get_track_objects (obj)) != NULL);
fail_unless ((tckobj = GES_TRACK_OBJECT (tckobjs->data)) != NULL);
fail_unless (ges_track_object_get_track (tckobj) == track);
assert_equals_uint64 (ges_track_object_get_duration (tckobj), 10);
g_list_free_full (tckobjs, g_object_unref);
/* Add a new layer and add objects to it */
fail_unless ((layer1 = ges_timeline_append_layer (timeline)) != NULL);
fail_unless (layer != layer1);
assert_equals_int (ges_timeline_layer_get_priority (layer1), 1);
fail_unless (ges_timeline_layer_add_object (layer1, obj1));
fail_unless ((tckobjs =
ges_timeline_object_get_track_objects (obj1)) != NULL);
fail_unless ((tckobj1 = GES_TRACK_OBJECT (tckobjs->data)) != NULL);
fail_unless (ges_track_object_get_track (tckobj1) == track);
assert_equals_uint64 (ges_track_object_get_duration (tckobj1), 10);
g_list_free_full (tckobjs, g_object_unref);
fail_unless (ges_timeline_layer_add_object (layer1, obj2));
fail_unless ((tckobjs =
ges_timeline_object_get_track_objects (obj2)) != NULL);
fail_unless ((tckobj2 = GES_TRACK_OBJECT (tckobjs->data)) != NULL);
fail_unless (ges_track_object_get_track (tckobj2) == track);
assert_equals_uint64 (ges_track_object_get_duration (tckobj2), 60);
g_list_free_full (tckobjs, g_object_unref);
/**
* Simple rippling obj to: 10
*
* New timeline:
* ------------
*
* inpoints 0-------
* | obj |
* time 10-------20
*
* 0-------- 0-----------
* | obj1 | | obj2 |
* 20--------30 60--------120
*/
fail_unless (ges_timeline_object_edit (obj, NULL, -1, GES_EDIT_MODE_RIPPLE,
GES_EDGE_NONE, 10) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 10, 0, 10);
CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
CHECK_OBJECT_PROPS (tckobj2, 60, 0, 60);
/* FIXME find a way to check that we are using the same MovingContext
* inside the GESTimeline */
fail_unless (ges_timeline_object_edit (obj1, NULL, 3, GES_EDIT_MODE_RIPPLE,
GES_EDGE_NONE, 40) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 10, 0, 10);
CHECK_OBJECT_PROPS (tckobj1, 40, 0, 10);
CHECK_OBJECT_PROPS (tckobj2, 80, 0, 60);
layer2 = ges_timeline_object_get_layer (obj1);
assert_equals_int (ges_timeline_layer_get_priority (layer2), 3);
/* obj2 should have moved layer too */
fail_unless (ges_timeline_object_get_layer (obj2) == layer2);
/* We got 2 reference to the same object, unref them */
g_object_unref (layer2);
g_object_unref (layer2);
/**
* Rippling obj1 back to: 20 (getting to the exact same timeline as before
*/
fail_unless (ges_timeline_object_edit (obj1, NULL, 1, GES_EDIT_MODE_RIPPLE,
GES_EDGE_NONE, 20) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 10, 0, 10);
CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
CHECK_OBJECT_PROPS (tckobj2, 60, 0, 60);
layer2 = ges_timeline_object_get_layer (obj1);
assert_equals_int (ges_timeline_layer_get_priority (layer2), 1);
/* obj2 should have moved layer too */
fail_unless (ges_timeline_object_get_layer (obj2) == layer2);
/* We got 2 reference to the same object, unref them */
g_object_unref (layer2);
g_object_unref (layer2);
/**
* Simple move obj to 27 and obj2 to 35
*
* New timeline:
* ------------
*
* inpoints 0-------
* | obj |
* time 27-------37
*
* 0-------- 0-----------
* | obj1 | | obj2 |
* 20--------30 35---------95
*/
fail_unless (ges_timeline_object_edit (obj, NULL, -1, GES_EDIT_MODE_NORMAL,
GES_EDGE_NONE, 27) == TRUE);
fail_unless (ges_timeline_object_edit (obj2, NULL, -1, GES_EDIT_MODE_NORMAL,
GES_EDGE_NONE, 35) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 27, 0, 10);
CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
CHECK_OBJECT_PROPS (tckobj2, 35, 0, 60);
/**
* Simple trimming start obj to: 32
*
* New timeline:
* ------------
*
* 5-------
* layer 0: | obj |
* 32-------37
*
* 0-------- 0-----------
* layer 1 | obj1 | | obj2 |
* 20--------30 35---------95
*/
fail_unless (ges_timeline_object_edit (obj, NULL, -1, GES_EDIT_MODE_TRIM,
GES_EDGE_START, 32) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 32, 5, 5);
CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
CHECK_OBJECT_PROPS (tckobj2, 35, 0, 60);
/* Ripple end obj to 35 and move to layer 2
* New timeline:
* ------------
*
* 0-------- 0-----------
* layer 1: | obj1 | | obj2 |
* 20--------30 35---------95
*
* 5------
* layer 2: | obj |
* 32------35
*/
fail_unless (ges_timeline_object_edit (obj, NULL, 2, GES_EDIT_MODE_RIPPLE,
GES_EDGE_END, 35) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 32, 5, 3);
CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
CHECK_OBJECT_PROPS (tckobj2, 35, 0, 60);
layer = ges_timeline_object_get_layer (obj);
assert_equals_int (ges_timeline_layer_get_priority (layer), 2);
g_object_unref (layer);
/* Roll end obj to 50
* New timeline:
* ------------
*
* 0-------- 0-----------
* layer 1: | obj1 | | obj2 |
* 20--------30 50---------95
*
* 5------
* layer 2: | obj |
* 32------50
*/
fail_unless (ges_timeline_object_edit (obj, NULL, 2, GES_EDIT_MODE_ROLL,
GES_EDGE_END, 50) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 32, 5, 18);
CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
CHECK_OBJECT_PROPS (tckobj2, 50, 15, 45);
layer = ges_timeline_object_get_layer (obj);
assert_equals_int (ges_timeline_layer_get_priority (layer), 2);
g_object_unref (layer);
/* Some more intensive roll testing */
for (i = 0; i < 20; i++) {
gint32 random = g_random_int_range (35, 94);
guint64 tck3_inpoint = random - 35;
fail_unless (ges_timeline_object_edit (obj, NULL, -1, GES_EDIT_MODE_ROLL,
GES_EDGE_END, random) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 32, 5, random - 32);
CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
CHECK_OBJECT_PROPS (tckobj2, random, tck3_inpoint, 95 - random);
}
/* Roll end obj back to 35
* New timeline:
* ------------
*
* 0-------- 0-----------
* layer 1: | obj1 | | obj2 |
* 20--------30 35---------95
*
* 5------
* layer 2: | obj |
* 32------35
*/
fail_unless (ges_timeline_object_edit (obj, NULL, 2, GES_EDIT_MODE_ROLL,
GES_EDGE_END, 35) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 32, 5, 3);
CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
CHECK_OBJECT_PROPS (tckobj2, 35, 0, 60);
layer = ges_timeline_object_get_layer (obj);
assert_equals_int (ges_timeline_layer_get_priority (layer), 2);
g_object_unref (layer);
/* Ripple obj end to 52
* New timeline:
* ------------
*
* 0-------- 0----------
* layer 1: | obj1 | | obj2 |
* 20-------30 52---------112
*
* 5------
* layer 2: | obj |
* 32------52
*
*/
/* Can not move to the first layer as obj2 should move to a layer with priority < 0 */
fail_unless (ges_timeline_object_edit (obj, NULL, 0, GES_EDIT_MODE_RIPPLE,
GES_EDGE_END, 52) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 32, 5, 20);
CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
CHECK_OBJECT_PROPS (tckobj2, 52, 0, 60)
layer = ges_timeline_object_get_layer (obj);
assert_equals_int (ges_timeline_layer_get_priority (layer), 2);
g_object_unref (layer);
/* Little check that we have 4 layers in the timeline */
layers = ges_timeline_get_layers (timeline);
assert_equals_int (g_list_length (layers), 4);
/* Some refcount checkings */
/* We have a reference to each layer in layers */
for (tmp = layers; tmp; tmp = tmp->next)
ASSERT_OBJECT_REFCOUNT (layer, "Layer", 2);
g_list_free_full (layers, g_object_unref);
/* We have 3 references:
* track + timeline + obj
*/
ASSERT_OBJECT_REFCOUNT (tckobj, "First tckobj", 3);
ASSERT_OBJECT_REFCOUNT (tckobj1, "Second tckobj", 3);
ASSERT_OBJECT_REFCOUNT (tckobj2, "Third tckobj", 3);
/* We have 1 ref:
* + layer */
ASSERT_OBJECT_REFCOUNT (obj, "First tlobj", 1);
ASSERT_OBJECT_REFCOUNT (obj1, "Second tlobj", 1);
ASSERT_OBJECT_REFCOUNT (obj2, "Third tlobj", 1);
/* Ripple obj end to 52
* New timeline:
* ------------
*
* 0-------- 0-----------
* layer 0: | obj1 | | obj2 |
* 20-------40 62----------112
*
* 5------
* layer 1: | obj |
* 42------60
*
*/
fail_unless (ges_timeline_object_edit (obj1, NULL, 0, GES_EDIT_MODE_RIPPLE,
GES_EDGE_END, 40) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 42, 5, 20);
CHECK_OBJECT_PROPS (tckobj1, 20, 0, 20);
CHECK_OBJECT_PROPS (tckobj2, 62, 0, 60);
/* Check that movement between layer has been done properly */
layer1 = ges_timeline_object_get_layer (obj);
layer = ges_timeline_object_get_layer (obj1);
assert_equals_int (ges_timeline_layer_get_priority (layer1), 1);
assert_equals_int (ges_timeline_layer_get_priority (layer), 0);
fail_unless (ges_timeline_object_get_layer (obj2) == layer);
g_object_unref (layer1);
/* We have 2 references to @layer that we do not need anymore */ ;
g_object_unref (layer);
g_object_unref (layer);
/* Trim obj start to 40
* New timeline:
* ------------
*
* 0-------- 0-----------
* layer 0: | obj1 | | obj2 |
* 20-------40 62---------112
*
* 0------
* layer 1: | obj |
* 40------62
*
*/
fail_unless (ges_timeline_object_edit (obj, NULL, -1, GES_EDIT_MODE_TRIM,
GES_EDGE_START, 40) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 40, 3, 22);
CHECK_OBJECT_PROPS (tckobj1, 20, 0, 20);
CHECK_OBJECT_PROPS (tckobj2, 62, 0, 60);
/* Roll obj end to 25
* New timeline:
* ------------
*
* 0-------- 0-----------
* layer 0: | obj1 | | obj2 |
* 20-------25 62---------112
*
* 0------
* layer 1: | obj |
* 25------62
*
*/
fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_ROLL,
GES_EDGE_END, 25) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
CHECK_OBJECT_PROPS (tckobj1, 20, 0, 5);
CHECK_OBJECT_PROPS (tckobj2, 62, 0, 60);
/* Make sure that not doing anything when not able to roll */
fail_unless (ges_timeline_object_edit (obj, NULL, -1, GES_EDIT_MODE_ROLL,
GES_EDGE_START, 65) == TRUE);
fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_ROLL,
GES_EDGE_END, 65) == TRUE, 0);
CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
CHECK_OBJECT_PROPS (tckobj1, 20, 0, 5);
CHECK_OBJECT_PROPS (tckobj2, 62, 0, 60);
/* Snaping to edge, so no move */
g_object_set (timeline, "snapping-distance", (guint64) 3, NULL);
fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_TRIM,
GES_EDGE_END, 27) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
CHECK_OBJECT_PROPS (tckobj1, 20, 0, 5);
CHECK_OBJECT_PROPS (tckobj2, 62, 0, 60);
/* Snaping to edge, so no move */
fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_TRIM,
GES_EDGE_END, 27) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
CHECK_OBJECT_PROPS (tckobj1, 20, 0, 5);
CHECK_OBJECT_PROPS (tckobj2, 62, 0, 60);
/**
* New timeline:
* ------------
* 0----------- 0-------------
* inpoints 0-------|-- obj || obj2 |
* | obj1 25-|------- 62 -----------122
* time 20----------30
*/
g_object_set (timeline, "snapping-distance", (guint64) 0, NULL);
fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_TRIM,
GES_EDGE_END, 30) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
CHECK_OBJECT_PROPS (tckobj2, 62, 0, 60);
/**
* New timeline
* ------------
* 0----------
* | obj |
* 25---------62
* inpoints 0----------------------- 10--------
* | obj1 || obj2 |
* time 20---------------------- 72 --------122
*/
/* Rolling involves only neighbours that are currently snapping */
fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_ROLL,
GES_EDGE_END, 62) == TRUE);
fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_ROLL,
GES_EDGE_END, 72) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
CHECK_OBJECT_PROPS (tckobj1, 20, 0, 52);
CHECK_OBJECT_PROPS (tckobj2, 72, 10, 50);
/* Test Snapping */
/**
* 0----------
* | obj |
* 25---------62
* inpoints 5--------------- 10--------
* | obj1 || obj2 |
* time 25------------- 72 --------122
*/
g_object_set (timeline, "snapping-distance", (guint64) 4, NULL);
fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_TRIM,
GES_EDGE_START, 28) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
CHECK_OBJECT_PROPS (tckobj1, 25, 5, 47);
CHECK_OBJECT_PROPS (tckobj2, 72, 10, 50);
/**
* 0----------
* | obj |
* 25---------62
* inpoints 5---------- 0---------
* | obj1 || obj2 |
* time 25-------- 62 --------122
*/
fail_unless (ges_timeline_object_edit (obj2, NULL, -1, GES_EDIT_MODE_ROLL,
GES_EDGE_START, 59) == TRUE);
CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
CHECK_OBJECT_PROPS (tckobj1, 25, 5, 37);
CHECK_OBJECT_PROPS (tckobj2, 62, 0, 60);
}
GST_END_TEST;
static Suite *
ges_suite (void)
{
Suite *s = suite_create ("ges-timeline-edition");
TCase *tc_chain = tcase_create ("timeline-edition");
suite_add_tcase (s, tc_chain);
tcase_add_test (tc_chain, test_basic_timeline_edition);
tcase_add_test (tc_chain, test_snapping);
tcase_add_test (tc_chain, test_timeline_edition_mode);
return s;
}
int
main (int argc, char **argv)
{
int nf;
Suite *s = ges_suite ();
SRunner *sr = srunner_create (s);
gst_check_init (&argc, &argv);
srunner_run_all (sr, CK_NORMAL);
nf = srunner_ntests_failed (sr);
srunner_free (sr);
return nf;
}

View file

@ -195,6 +195,83 @@ GST_START_TEST (test_object_properties_unlocked)
}
GST_END_TEST;
GST_START_TEST (test_split_object)
{
GESTrack *track;
GESTrackObject *trackobject, *splittckobj;
GESTimelineObject *object, *splitobj;
GList *splittckobjs;
ges_init ();
track = ges_track_new (GES_TRACK_TYPE_CUSTOM, GST_CAPS_ANY);
fail_unless (track != NULL);
object =
(GESTimelineObject *) ges_custom_timeline_source_new (my_fill_track_func,
NULL);
fail_unless (object != NULL);
/* Set some properties */
g_object_set (object, "start", (guint64) 42, "duration", (guint64) 50,
"in-point", (guint64) 12, NULL);
assert_equals_uint64 (GES_TIMELINE_OBJECT_START (object), 42);
assert_equals_uint64 (GES_TIMELINE_OBJECT_DURATION (object), 50);
assert_equals_uint64 (GES_TIMELINE_OBJECT_INPOINT (object), 12);
trackobject = ges_timeline_object_create_track_object (object, track);
ges_timeline_object_add_track_object (object, trackobject);
fail_unless (trackobject != NULL);
fail_unless (ges_track_object_set_track (trackobject, track));
/* Check that trackobject has the same properties */
assert_equals_uint64 (GES_TRACK_OBJECT_START (trackobject), 42);
assert_equals_uint64 (GES_TRACK_OBJECT_DURATION (trackobject), 50);
assert_equals_uint64 (GES_TRACK_OBJECT_INPOINT (trackobject), 12);
/* And let's also check that it propagated correctly to GNonLin */
gnl_object_check (ges_track_object_get_gnlobject (trackobject), 42, 50, 12,
50, 0, TRUE);
splitobj = ges_timeline_object_split (object, 67);
fail_unless (GES_IS_TIMELINE_OBJECT (splitobj));
assert_equals_uint64 (GES_TIMELINE_OBJECT_START (object), 42);
assert_equals_uint64 (GES_TIMELINE_OBJECT_DURATION (object), 25);
assert_equals_uint64 (GES_TIMELINE_OBJECT_INPOINT (object), 12);
assert_equals_uint64 (GES_TIMELINE_OBJECT_START (splitobj), 67);
assert_equals_uint64 (GES_TIMELINE_OBJECT_DURATION (splitobj), 25);
assert_equals_uint64 (GES_TIMELINE_OBJECT_INPOINT (splitobj), 37);
splittckobjs = ges_timeline_object_get_track_objects (splitobj);
fail_unless_equals_int (g_list_length (splittckobjs), 1);
splittckobj = GES_TRACK_OBJECT (splittckobjs->data);
fail_unless (GES_IS_TRACK_OBJECT (splittckobj));
assert_equals_uint64 (GES_TRACK_OBJECT_START (splittckobj), 67);
assert_equals_uint64 (GES_TRACK_OBJECT_DURATION (splittckobj), 25);
assert_equals_uint64 (GES_TRACK_OBJECT_INPOINT (splittckobj), 37);
fail_unless (splittckobj != trackobject);
fail_unless (splitobj != object);
/* We own the only ref */
ASSERT_OBJECT_REFCOUNT (splitobj, "splitobj", 1);
/* 1 ref for the TimelineObject, 1 ref for the Track and 1 in splittckobjs */
ASSERT_OBJECT_REFCOUNT (splittckobj, "splittckobj", 3);
g_object_unref (track);
g_object_unref (splitobj);
g_object_unref (object);
ASSERT_OBJECT_REFCOUNT (splittckobj, "splittckobj", 1);
g_list_free_full (splittckobjs, g_object_unref);
}
GST_END_TEST;
static Suite *
ges_suite (void)
{
@ -205,6 +282,7 @@ ges_suite (void)
tcase_add_test (tc_chain, test_object_properties);
tcase_add_test (tc_chain, test_object_properties_unlocked);
tcase_add_test (tc_chain, test_split_object);
return s;
}