mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-27 18:50:48 +00:00
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:
commit
b1a8596256
18 changed files with 3153 additions and 176 deletions
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
1154
ges/ges-timeline.c
1154
ges/ges-timeline.c
File diff suppressed because it is too large
Load diff
|
@ -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 (¶ms[n_params].value, specs[n]->value_type);
|
||||
g_object_get_property (G_OBJECT (object), specs[n]->name,
|
||||
¶ms[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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -22,6 +22,7 @@ check_PROGRAMS = \
|
|||
ges/filesource \
|
||||
ges/simplelayer \
|
||||
ges/timelineobject \
|
||||
ges/timelineedition \
|
||||
ges/titles\
|
||||
ges/transition \
|
||||
ges/overlays\
|
||||
|
|
|
@ -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);
|
||||
|
|
975
tests/check/ges/timelineedition.c
Normal file
975
tests/check/ges/timelineedition.c
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue