1 /* GStreamer Editing Services
2 * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
3 * 2009 Nokia Corporation
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
22 * SECTION:ges-timeline-object
23 * @short_description: Base Class for objects in a #GESTimelineLayer
25 * A #GESTimelineObject is a 'natural' object which controls one or more
26 * #GESTrackObject(s) in one or more #GESTrack(s).
28 * Keeps a reference to the #GESTrackObject(s) it created and
29 * sets/updates their properties.
32 #include "ges-timeline-object.h"
34 #include "ges-internal.h"
39 ges_timeline_object_fill_track_object_func (GESTimelineObject * object,
40 GESTrackObject * trackobj, GstElement * gnlobj);
43 ges_timeline_object_create_track_objects_func (GESTimelineObject
44 * object, GESTrack * track);
46 void default_set_max_duration (GESTimelineObject * object, guint64 maxduration);
49 track_object_start_changed_cb (GESTrackObject * child,
50 GParamSpec * arg G_GNUC_UNUSED, GESTimelineObject * object);
52 track_object_inpoint_changed_cb (GESTrackObject * child,
53 GParamSpec * arg G_GNUC_UNUSED, GESTimelineObject * object);
55 track_object_duration_changed_cb (GESTrackObject * child,
56 GParamSpec * arg G_GNUC_UNUSED, GESTimelineObject * object);
58 track_object_priority_changed_cb (GESTrackObject * child,
59 GParamSpec * arg G_GNUC_UNUSED, GESTimelineObject * object);
60 static void update_height (GESTimelineObject * object);
62 static gint sort_track_effects (gpointer a, gpointer b,
63 GESTimelineObject * object);
65 get_layer_priorities (GESTimelineLayer * layer, guint32 * layer_min_gnl_prio,
66 guint32 * layer_max_gnl_prio);
69 ges_timeline_object_set_start_internal (GESTimelineObject * object,
71 static gboolean ges_timeline_object_set_inpoint_internal (GESTimelineObject *
72 object, guint64 inpoint);
73 static gboolean ges_timeline_object_set_duration_internal (GESTimelineObject *
74 object, guint64 duration);
75 static gboolean ges_timeline_object_set_priority_internal (GESTimelineObject *
76 object, guint32 priority);
78 static GESTimelineObject *ges_timeline_object_copy (GESTimelineObject * object,
81 G_DEFINE_ABSTRACT_TYPE (GESTimelineObject, ges_timeline_object,
82 G_TYPE_INITIALLY_UNOWNED);
84 /* Mapping of relationship between a TimelineObject and the TrackObjects
87 * NOTE : how do we make this public in the future ?
91 GESTrackObject *object;
93 gint64 duration_offset;
94 gint64 inpoint_offset;
95 gint32 priority_offset;
98 guint duration_notifyid;
99 guint inpoint_notifyid;
100 guint priority_notifyid;
102 /* track mapping ?? */
110 TRACK_OBJECT_REMOVED,
114 static guint ges_timeline_object_signals[LAST_SIGNAL] = { 0 };
116 struct _GESTimelineObjectPrivate
119 GESTimelineLayer *layer;
122 /* A list of TrackObject controlled by this TimelineObject sorted by
126 /* Set to TRUE when the timelineobject is doing updates of track object
127 * properties so we don't end up in infinite property update loops
129 gboolean ignore_notifies;
138 GESTrackObject *initiated_move;
140 /* The formats supported by this TimelineObject */
141 GESTrackType supportedformats;
153 PROP_SUPPORTED_FORMATS,
158 static GParamSpec *properties[PROP_LAST];
161 ges_timeline_object_get_property (GObject * object, guint property_id,
162 GValue * value, GParamSpec * pspec)
164 GESTimelineObject *tobj = GES_TIMELINE_OBJECT (object);
166 switch (property_id) {
168 g_value_set_uint64 (value, tobj->start);
171 g_value_set_uint64 (value, tobj->inpoint);
174 g_value_set_uint64 (value, tobj->duration);
177 g_value_set_uint (value, tobj->priority);
180 g_value_set_uint (value, tobj->height);
183 g_value_set_object (value, tobj->priv->layer);
185 case PROP_SUPPORTED_FORMATS:
186 g_value_set_flags (value, tobj->priv->supportedformats);
188 case PROP_MAX_DURATION:
189 g_value_set_uint64 (value, tobj->priv->maxduration);
192 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
197 ges_timeline_object_set_property (GObject * object, guint property_id,
198 const GValue * value, GParamSpec * pspec)
200 GESTimelineObject *tobj = GES_TIMELINE_OBJECT (object);
202 switch (property_id) {
204 ges_timeline_object_set_start_internal (tobj, g_value_get_uint64 (value));
207 ges_timeline_object_set_inpoint_internal (tobj,
208 g_value_get_uint64 (value));
211 ges_timeline_object_set_duration_internal (tobj,
212 g_value_get_uint64 (value));
215 ges_timeline_object_set_priority_internal (tobj,
216 g_value_get_uint (value));
218 case PROP_SUPPORTED_FORMATS:
219 ges_timeline_object_set_supported_formats (tobj,
220 g_value_get_flags (value));
222 case PROP_MAX_DURATION:
223 ges_timeline_object_set_max_duration (tobj, g_value_get_uint64 (value));
226 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
231 ges_timeline_object_class_init (GESTimelineObjectClass * klass)
233 GObjectClass *object_class = G_OBJECT_CLASS (klass);
235 g_type_class_add_private (klass, sizeof (GESTimelineObjectPrivate));
237 object_class->get_property = ges_timeline_object_get_property;
238 object_class->set_property = ges_timeline_object_set_property;
239 klass->create_track_objects = ges_timeline_object_create_track_objects_func;
240 klass->set_max_duration = default_set_max_duration;
241 klass->track_object_added = NULL;
242 klass->track_object_released = NULL;
245 * GESTimelineObject:start
247 * The position of the object in the #GESTimelineLayer (in nanoseconds).
249 properties[PROP_START] = g_param_spec_uint64 ("start", "Start",
250 "The position in the container", 0, G_MAXUINT64, 0, G_PARAM_READWRITE);
251 g_object_class_install_property (object_class, PROP_START,
252 properties[PROP_START]);
255 * GESTimelineObject:in-point
257 * The in-point at which this #GESTimelineObject will start outputting data
258 * from its contents (in nanoseconds).
260 * Ex : an in-point of 5 seconds means that the first outputted buffer will
261 * be the one located 5 seconds in the controlled resource.
263 properties[PROP_INPOINT] =
264 g_param_spec_uint64 ("in-point", "In-point", "The in-point", 0,
265 G_MAXUINT64, 0, G_PARAM_READWRITE);
266 g_object_class_install_property (object_class, PROP_INPOINT,
267 properties[PROP_INPOINT]);
270 * GESTimelineObject:duration
272 * The duration (in nanoseconds) which will be used in the container #GESTrack
273 * starting from 'in-point'.
275 properties[PROP_DURATION] =
276 g_param_spec_uint64 ("duration", "Duration", "The duration to use", 0,
277 G_MAXUINT64, GST_CLOCK_TIME_NONE, G_PARAM_READWRITE);
278 g_object_class_install_property (object_class, PROP_DURATION,
279 properties[PROP_DURATION]);
282 * GESTimelineObject:priority
284 * The layer priority of the timeline object.
286 properties[PROP_PRIORITY] = g_param_spec_uint ("priority", "Priority",
287 "The priority of the object", 0, G_MAXUINT, 0, G_PARAM_READWRITE);
288 g_object_class_install_property (object_class, PROP_PRIORITY,
289 properties[PROP_PRIORITY]);
292 * GESTimelineObject:height
294 * The span of layer priorities which this object occupies.
296 properties[PROP_HEIGHT] = g_param_spec_uint ("height", "Height",
297 "The span of priorities this object occupies", 0, G_MAXUINT, 1,
299 g_object_class_install_property (object_class, PROP_HEIGHT,
300 properties[PROP_HEIGHT]);
303 * GESTimelineObject:supported-formats:
305 * The formats supported by the object.
309 properties[PROP_SUPPORTED_FORMATS] = g_param_spec_flags ("supported-formats",
310 "Supported formats", "Formats supported by the file",
311 GES_TYPE_TRACK_TYPE, GES_TRACK_TYPE_UNKNOWN,
312 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
314 g_object_class_install_property (object_class, PROP_SUPPORTED_FORMATS,
315 properties[PROP_SUPPORTED_FORMATS]);
318 * GESTimelineObject:layer
320 * The GESTimelineLayer where this object is being used.
322 properties[PROP_LAYER] = g_param_spec_object ("layer", "Layer",
323 "The GESTimelineLayer where this object is being used.",
324 GES_TYPE_TIMELINE_LAYER, G_PARAM_READABLE);
325 g_object_class_install_property (object_class, PROP_LAYER,
326 properties[PROP_LAYER]);
329 * GESTimelineObject::effect-added
330 * @object: the #GESTimelineObject
331 * @effect: the #GESTrackEffect that was added.
333 * Will be emitted after an effect was added to the object.
337 ges_timeline_object_signals[EFFECT_ADDED] =
338 g_signal_new ("effect-added", G_TYPE_FROM_CLASS (klass),
339 G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_generic,
340 G_TYPE_NONE, 1, GES_TYPE_TRACK_EFFECT);
343 * GESTimelineObject::effect-removed
344 * @object: the #GESTimelineObject
345 * @effect: the #GESTrackEffect that was added.
347 * Will be emitted after an effect was remove from the object.
351 ges_timeline_object_signals[EFFECT_REMOVED] =
352 g_signal_new ("effect-removed", G_TYPE_FROM_CLASS (klass),
353 G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_generic,
354 G_TYPE_NONE, 1, GES_TYPE_TRACK_EFFECT);
357 * GESTimelineObject::track-object-added
358 * @object: the #GESTimelineObject
359 * @tckobj: the #GESTrackObject that was added.
361 * Will be emitted after a track object was added to the object.
365 ges_timeline_object_signals[TRACK_OBJECT_ADDED] =
366 g_signal_new ("track-object-added", G_TYPE_FROM_CLASS (klass),
367 G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_generic,
368 G_TYPE_NONE, 1, GES_TYPE_TRACK_OBJECT);
371 * GESTimelineObject::track-object-removed
372 * @object: the #GESTimelineObject
373 * @tckobj: the #GESTrackObject that was removed.
375 * Will be emitted after a track object was removed from @object.
379 ges_timeline_object_signals[TRACK_OBJECT_REMOVED] =
380 g_signal_new ("track-object-removed", G_TYPE_FROM_CLASS (klass),
381 G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_generic,
382 G_TYPE_NONE, 1, GES_TYPE_TRACK_OBJECT);
385 * GESTimelineObject:max-duration:
387 * The maximum duration (in nanoseconds) of the #GESTimelineObject.
391 g_object_class_install_property (object_class, PROP_MAX_DURATION,
392 g_param_spec_uint64 ("max-duration", "Maximum duration",
393 "The duration of the object", 0, G_MAXUINT64, G_MAXUINT64,
394 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
396 klass->need_fill_track = TRUE;
397 klass->snaps = FALSE;
401 ges_timeline_object_init (GESTimelineObject * self)
403 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
404 GES_TYPE_TIMELINE_OBJECT, GESTimelineObjectPrivate);
405 self->duration = GST_SECOND;
407 self->priv->trackobjects = NULL;
408 self->priv->layer = NULL;
409 self->priv->nb_effects = 0;
410 self->priv->is_moving = FALSE;
411 self->priv->maxduration = G_MAXUINT64;
415 * ges_timeline_object_create_track_object:
416 * @object: The origin #GESTimelineObject
417 * @track: The #GESTrack to create a #GESTrackObject for.
419 * Creates a #GESTrackObject for the provided @track. The timeline object
420 * keep a reference to the newly created trackobject, you therefore need to
421 * call @ges_timeline_object_release_track_object when you are done with it.
423 * Returns: (transfer none): A #GESTrackObject. Returns NULL if the #GESTrackObject could not
428 ges_timeline_object_create_track_object (GESTimelineObject * object,
431 GESTimelineObjectClass *class;
434 g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), NULL);
435 g_return_val_if_fail (GES_IS_TRACK (track), NULL);
437 class = GES_TIMELINE_OBJECT_GET_CLASS (object);
439 if (G_UNLIKELY (class->create_track_object == NULL)) {
440 GST_ERROR ("No 'create_track_object' implementation available");
444 res = class->create_track_object (object, track);
450 * ges_timeline_object_create_track_objects:
451 * @object: The origin #GESTimelineObject
452 * @track: The #GESTrack to create each #GESTrackObject for.
454 * Creates all #GESTrackObjects supported by this object and adds them to the
455 * provided track. The track is responsible for calling
456 * #ges_timeline_release_track_object on these objects when it is finished
459 * Returns: %TRUE if each track object was created successfully, or %FALSE if an
464 ges_timeline_object_create_track_objects (GESTimelineObject * object,
467 GESTimelineObjectClass *klass;
469 g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
470 g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
472 klass = GES_TIMELINE_OBJECT_GET_CLASS (object);
474 if (!(klass->create_track_objects)) {
475 GST_WARNING ("no GESTimelineObject::create_track_objects implentation");
479 return klass->create_track_objects (object, track);
482 /* Default implementation of default_set_max_duration */
484 default_set_max_duration (GESTimelineObject * object, guint64 maxduration)
487 for (tmp = object->priv->trackobjects; tmp; tmp = g_list_next (tmp))
488 g_object_set (tmp->data, "max-duration", maxduration, NULL);
492 * default implementation of GESTimelineObjectClass::create_track_objects
495 ges_timeline_object_create_track_objects_func (GESTimelineObject * object,
498 GESTrackObject *result;
500 result = ges_timeline_object_create_track_object (object, track);
502 GST_DEBUG ("Did not create track object");
506 if (ges_timeline_object_add_track_object (object, result) == FALSE)
509 return ges_track_add_object (track, result);
513 * ges_timeline_object_add_track_object:
514 * @object: a #GESTimelineObject
515 * @trobj: the GESTrackObject
517 * Add a track object to the timeline object. Should only be called by
518 * subclasses implementing the create_track_objects (plural) vmethod.
520 * Takes a reference on @trobj.
522 * Returns: %TRUE on success, %FALSE on failure.
526 ges_timeline_object_add_track_object (GESTimelineObject * object, GESTrackObject
529 ObjectMapping *mapping;
531 guint max_prio, min_prio;
532 GESTimelineObjectPrivate *priv = object->priv;
533 gboolean is_effect = GES_IS_TRACK_EFFECT (trobj);
534 GESTimelineObjectClass *klass = GES_TIMELINE_OBJECT_GET_CLASS (object);
536 g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
537 g_return_val_if_fail (GES_IS_TRACK_OBJECT (trobj), FALSE);
539 GST_LOG ("Got a TrackObject : %p , setting the timeline object as its"
540 "creator. Is a TrackEffect %i", trobj, is_effect);
545 ges_track_object_set_timeline_object (trobj, object);
547 g_object_ref (trobj);
549 mapping = g_slice_new0 (ObjectMapping);
550 mapping->object = trobj;
551 priv->mappings = g_list_append (priv->mappings, mapping);
553 GST_DEBUG ("Adding TrackObject to the list of controlled track objects");
554 /* We steal the initial reference */
556 GST_DEBUG ("Setting properties on newly created TrackObject");
558 mapping->priority_offset = priv->nb_effects;
560 /* If the trackobject is an effect:
561 * - We add it on top of the list of TrackEffect
562 * - We put all TrackObject present in the TimelineObject
563 * which are not TrackEffect on top of them
565 * FIXME: Let the full control over priorities to the user
569 ("Moving non on top effect under other TrackObject-s, nb effects %i",
571 for (tmp = g_list_nth (priv->trackobjects, priv->nb_effects); tmp;
573 GESTrackObject *tmpo = GES_TRACK_OBJECT (tmp->data);
575 /* We make sure not to move the entire #TimelineObject */
576 ges_track_object_set_locked (tmpo, FALSE);
577 ges_track_object_set_priority (tmpo,
578 ges_track_object_get_priority (tmpo) + 1);
579 ges_track_object_set_locked (tmpo, TRUE);
585 object->priv->trackobjects =
586 g_list_insert_sorted_with_data (object->priv->trackobjects, trobj,
587 (GCompareDataFunc) sort_track_effects, object);
589 ges_track_object_set_start (trobj, object->start);
590 ges_track_object_set_duration (trobj, object->duration);
591 ges_track_object_set_inpoint (trobj, object->inpoint);
592 ges_track_object_set_max_duration (trobj, object->priv->maxduration);
594 if (klass->track_object_added) {
595 GST_DEBUG ("Calling track_object_added subclass method");
596 klass->track_object_added (object, trobj);
598 GST_DEBUG ("%s doesn't have any track_object_added vfunc implementation",
599 G_OBJECT_CLASS_NAME (klass));
602 /* Listen to all property changes */
603 mapping->start_notifyid =
604 g_signal_connect (G_OBJECT (trobj), "notify::start",
605 G_CALLBACK (track_object_start_changed_cb), object);
606 mapping->duration_notifyid =
607 g_signal_connect (G_OBJECT (trobj), "notify::duration",
608 G_CALLBACK (track_object_duration_changed_cb), object);
609 mapping->inpoint_notifyid =
610 g_signal_connect (G_OBJECT (trobj), "notify::in-point",
611 G_CALLBACK (track_object_inpoint_changed_cb), object);
612 mapping->priority_notifyid =
613 g_signal_connect (G_OBJECT (trobj), "notify::priority",
614 G_CALLBACK (track_object_priority_changed_cb), object);
616 get_layer_priorities (priv->layer, &min_prio, &max_prio);
617 ges_track_object_set_priority (trobj, min_prio + object->priority
618 + mapping->priority_offset);
620 GST_DEBUG ("Returning trobj:%p", trobj);
621 if (!GES_IS_TRACK_EFFECT (trobj)) {
622 g_signal_emit (object, ges_timeline_object_signals[TRACK_OBJECT_ADDED], 0,
623 GES_TRACK_OBJECT (trobj));
625 /* emit 'effect-added' */
626 g_signal_emit (object, ges_timeline_object_signals[EFFECT_ADDED], 0,
627 GES_TRACK_EFFECT (trobj));
634 * ges_timeline_object_release_track_object:
635 * @object: a #GESTimelineObject
636 * @trackobject: the #GESTrackObject to release
638 * Release the @trackobject from the control of @object.
640 * Returns: %TRUE if the @trackobject was properly released, else %FALSE.
643 ges_timeline_object_release_track_object (GESTimelineObject * object,
644 GESTrackObject * trackobject)
647 ObjectMapping *mapping = NULL;
648 GESTimelineObjectClass *klass;
650 g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
651 g_return_val_if_fail (GES_IS_TRACK_OBJECT (trackobject), FALSE);
653 GST_DEBUG ("object:%p, trackobject:%p", object, trackobject);
654 klass = GES_TIMELINE_OBJECT_GET_CLASS (object);
656 if (!(g_list_find (object->priv->trackobjects, trackobject))) {
657 GST_WARNING ("TrackObject isn't controlled by this object");
661 for (tmp = object->priv->mappings; tmp; tmp = tmp->next) {
662 mapping = (ObjectMapping *) tmp->data;
663 if (mapping->object == trackobject)
667 if (tmp && mapping) {
669 /* Disconnect all notify listeners */
670 g_signal_handler_disconnect (trackobject, mapping->start_notifyid);
671 g_signal_handler_disconnect (trackobject, mapping->duration_notifyid);
672 g_signal_handler_disconnect (trackobject, mapping->inpoint_notifyid);
673 g_signal_handler_disconnect (trackobject, mapping->priority_notifyid);
675 g_slice_free (ObjectMapping, mapping);
677 object->priv->mappings = g_list_delete_link (object->priv->mappings, tmp);
680 object->priv->trackobjects =
681 g_list_remove (object->priv->trackobjects, trackobject);
683 if (GES_IS_TRACK_EFFECT (trackobject)) {
684 /* emit 'object-removed' */
685 object->priv->nb_effects--;
686 g_signal_emit (object, ges_timeline_object_signals[EFFECT_REMOVED], 0,
687 GES_TRACK_EFFECT (trackobject));
689 g_signal_emit (object, ges_timeline_object_signals[TRACK_OBJECT_REMOVED], 0,
690 GES_TRACK_OBJECT (trackobject));
692 ges_track_object_set_timeline_object (trackobject, NULL);
694 GST_DEBUG ("Removing reference to track object %p", trackobject);
696 if (klass->track_object_released) {
697 GST_DEBUG ("Calling track_object_released subclass method");
698 klass->track_object_released (object, trackobject);
701 g_object_unref (trackobject);
703 /* FIXME : resync properties ? */
709 ges_timeline_object_set_layer (GESTimelineObject * object,
710 GESTimelineLayer * layer)
712 GST_DEBUG ("object:%p, layer:%p", object, layer);
714 object->priv->layer = layer;
718 ges_timeline_object_fill_track_object (GESTimelineObject * object,
719 GESTrackObject * trackobj, GstElement * gnlobj)
721 GESTimelineObjectClass *class;
724 GST_DEBUG ("object:%p, trackobject:%p, gnlobject:%p",
725 object, trackobj, gnlobj);
727 class = GES_TIMELINE_OBJECT_GET_CLASS (object);
729 if (class->need_fill_track) {
730 if (G_UNLIKELY (class->fill_track_object == NULL)) {
731 GST_WARNING ("No 'fill_track_object' implementation available");
735 res = class->fill_track_object (object, trackobj, gnlobj);
738 GST_DEBUG ("Returning res:%d", res);
744 ges_timeline_object_fill_track_object_func (GESTimelineObject * object,
745 GESTrackObject * trackobj, GstElement * gnlobj)
747 GST_WARNING ("No 'fill_track_object' implementation !");
752 static ObjectMapping *
753 find_object_mapping (GESTimelineObject * object, GESTrackObject * child)
757 for (tmp = object->priv->mappings; tmp; tmp = tmp->next) {
758 ObjectMapping *map = (ObjectMapping *) tmp->data;
759 if (map->object == child)
767 ges_timeline_object_set_start_internal (GESTimelineObject * object,
773 GESTimeline *timeline = NULL;
774 GESTimelineObjectPrivate *priv = object->priv;
775 gboolean snap = FALSE;
777 g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
779 GST_DEBUG ("object:%p, start:%" GST_TIME_FORMAT,
780 object, GST_TIME_ARGS (start));
782 /* If the class has snapping enabled and the object is in a timeline,
784 if (priv->layer && GES_TIMELINE_OBJECT_GET_CLASS (object)->snaps)
785 timeline = ges_timeline_layer_get_timeline (object->priv->layer);
786 snap = timeline && priv->initiated_move == NULL ? TRUE : FALSE;
788 object->priv->ignore_notifies = TRUE;
790 for (tmp = object->priv->trackobjects; tmp; tmp = g_list_next (tmp)) {
791 tr = (GESTrackObject *) tmp->data;
792 map = find_object_mapping (object, tr);
794 if (ges_track_object_is_locked (tr) && tr != object->priv->initiated_move) {
795 gint64 new_start = start - map->start_offset;
797 /* Move the child... */
799 GST_ERROR ("Trying to set start to a negative value %" GST_TIME_FORMAT,
800 GST_TIME_ARGS (-(start + map->start_offset)));
804 /* Make the snapping happen if in a timeline */
806 ges_timeline_move_object_simple (timeline, tr, NULL, GES_EDGE_NONE,
809 ges_track_object_set_start (tr, start);
811 /* ... or update the offset */
812 map->start_offset = start - tr->start;
816 object->priv->ignore_notifies = FALSE;
818 object->start = start;
823 * ges_timeline_object_set_start:
824 * @object: a #GESTimelineObject
825 * @start: the position in #GstClockTime
827 * Set the position of the object in its containing layer
829 * Note that if the timeline snap-distance property of the timeline containing
830 * @object is set, @object will properly snap to its neighboors.
833 ges_timeline_object_set_start (GESTimelineObject * object, guint64 start)
835 if (ges_timeline_object_set_start_internal (object, start))
836 #if GLIB_CHECK_VERSION(2,26,0)
837 g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_START]);
839 g_object_notify (G_OBJECT (object), "start");
844 ges_timeline_object_set_inpoint_internal (GESTimelineObject * object,
850 g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
852 GST_DEBUG ("object:%p, inpoint:%" GST_TIME_FORMAT,
853 object, GST_TIME_ARGS (inpoint));
855 for (tmp = object->priv->trackobjects; tmp; tmp = g_list_next (tmp)) {
856 tr = (GESTrackObject *) tmp->data;
858 if (ges_track_object_is_locked (tr))
859 /* call set_inpoint on each trackobject */
860 ges_track_object_set_inpoint (tr, inpoint);
863 object->inpoint = inpoint;
868 * ges_timeline_object_set_inpoint:
869 * @object: a #GESTimelineObject
870 * @inpoint: the in-point in #GstClockTime
872 * Set the in-point, that is the moment at which the @object will start
873 * outputting data from its contents.
876 ges_timeline_object_set_inpoint (GESTimelineObject * object, guint64 inpoint)
878 if (ges_timeline_object_set_inpoint_internal (object, inpoint))
879 #if GLIB_CHECK_VERSION(2,26,0)
880 g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_INPOINT]);
882 g_object_notify (G_OBJECT (object), "in-point");
887 ges_timeline_object_set_duration_internal (GESTimelineObject * object,
892 GESTimeline *timeline = NULL;
893 GESTimelineObjectPrivate *priv = object->priv;
894 gboolean snap = FALSE;
896 g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
898 GST_DEBUG ("object:%p, duration:%" GST_TIME_FORMAT,
899 object, GST_TIME_ARGS (duration));
901 if (priv->layer && GES_TIMELINE_OBJECT_GET_CLASS (object)->snaps)
902 timeline = ges_timeline_layer_get_timeline (object->priv->layer);
904 /* If the class has snapping enabled, the object is in a timeline,
905 * and we are not following a moved TrackObject, we snap */
906 snap = timeline && priv->initiated_move == NULL ? TRUE : FALSE;
908 object->priv->ignore_notifies = TRUE;
909 for (tmp = object->priv->trackobjects; tmp; tmp = g_list_next (tmp)) {
910 tr = (GESTrackObject *) tmp->data;
912 if (ges_track_object_is_locked (tr)) {
913 /* call set_duration on each trackobject
914 * and make the snapping happen if in a timeline */
916 ges_timeline_trim_object_simple (timeline, tr, NULL, GES_EDGE_END,
917 tr->start + duration, TRUE);
919 ges_track_object_set_duration (tr, duration);
922 object->priv->ignore_notifies = FALSE;
924 object->duration = duration;
929 * ges_timeline_object_set_duration:
930 * @object: a #GESTimelineObject
931 * @duration: the duration in #GstClockTime
933 * Set the duration of the object
935 * Note that if the timeline snap-distance property of the timeline containing
936 * @object is set, @object will properly snap to its neighboors.
939 ges_timeline_object_set_duration (GESTimelineObject * object, guint64 duration)
941 if (ges_timeline_object_set_duration_internal (object, duration))
942 #if GLIB_CHECK_VERSION(2,26,0)
943 g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_DURATION]);
945 g_object_notify (G_OBJECT (object), "duration");
950 ges_timeline_object_set_priority_internal (GESTimelineObject * object,
956 GESTimelineObjectPrivate *priv;
957 guint32 layer_min_gnl_prio, layer_max_gnl_prio;
959 g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
961 GST_DEBUG ("object:%p, priority:%" G_GUINT32_FORMAT, object, priority);
965 get_layer_priorities (priv->layer, &layer_min_gnl_prio, &layer_max_gnl_prio);
967 priv->ignore_notifies = TRUE;
968 for (tmp = priv->trackobjects; tmp; tmp = g_list_next (tmp)) {
969 tr = (GESTrackObject *) tmp->data;
970 map = find_object_mapping (object, tr);
972 if (ges_track_object_is_locked (tr)) {
973 guint32 real_tck_prio;
975 /* Move the child... */
976 real_tck_prio = layer_min_gnl_prio + priority + map->priority_offset;
978 if (real_tck_prio > layer_max_gnl_prio) {
979 GST_WARNING ("%p priority of %i, is outside of the its containing "
980 "layer space. (%d/%d) setting it to the maximum it can be", object,
981 priority, layer_min_gnl_prio, layer_max_gnl_prio);
983 real_tck_prio = layer_max_gnl_prio;
986 ges_track_object_set_priority (tr, real_tck_prio);
989 /* ... or update the offset */
990 map->priority_offset = tr->priority - layer_min_gnl_prio + priority;
994 priv->trackobjects = g_list_sort_with_data (priv->trackobjects,
995 (GCompareDataFunc) sort_track_effects, object);
996 priv->ignore_notifies = FALSE;
998 object->priority = priority;
1003 * ges_timeline_object_set_moving_from_layer:
1004 * @object: a #GESTimelineObject
1005 * @is_moving: %TRUE if you want to start moving @object to another layer
1006 * %FALSE when you finished moving it.
1008 * Sets the object in a moving to layer state. You might rather use the
1009 * ges_timeline_object_move_to_layer function to move #GESTimelineObject-s
1010 * from a layer to another.
1013 ges_timeline_object_set_moving_from_layer (GESTimelineObject * object,
1016 g_return_if_fail (GES_IS_TIMELINE_OBJECT (object));
1018 object->priv->is_moving = is_moving;
1022 * ges_timeline_object_is_moving_from_layer:
1023 * @object: a #GESTimelineObject
1025 * Tells you if the object is currently moving from a layer to another.
1026 * You might rather use the ges_timeline_object_move_to_layer function to
1027 * move #GESTimelineObject-s from a layer to another.
1030 * Returns: %TRUE if @object is currently moving from its current layer
1034 ges_timeline_object_is_moving_from_layer (GESTimelineObject * object)
1036 g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
1038 return object->priv->is_moving;
1042 * ges_timeline_object_move_to_layer:
1043 * @object: a #GESTimelineObject
1044 * @layer: the new #GESTimelineLayer
1046 * Moves @object to @layer. If @object is not in any layer, it adds it to
1047 * @layer, else, it removes it from its current layer, and adds it to @layer.
1049 * Returns: %TRUE if @object could be moved %FALSE otherwize
1052 ges_timeline_object_move_to_layer (GESTimelineObject * object, GESTimelineLayer
1056 GESTimelineLayer *current_layer;
1058 g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
1059 g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), FALSE);
1061 current_layer = object->priv->layer;
1063 if (current_layer == NULL) {
1064 GST_DEBUG ("Not moving %p, only adding it to %p", object, layer);
1066 return ges_timeline_layer_add_object (layer, object);
1069 GST_DEBUG_OBJECT (object, "moving to layer %p, priority: %d", layer,
1070 ges_timeline_layer_get_priority (layer));
1072 object->priv->is_moving = TRUE;
1073 g_object_ref (object);
1074 ret = ges_timeline_layer_remove_object (current_layer, object);
1077 g_object_unref (object);
1081 ret = ges_timeline_layer_add_object (layer, object);
1082 object->priv->is_moving = FALSE;
1084 g_object_unref (object);
1090 * ges_timeline_object_set_priority:
1091 * @object: a #GESTimelineObject
1092 * @priority: the priority
1094 * Sets the priority of the object within the containing layer
1097 ges_timeline_object_set_priority (GESTimelineObject * object, guint priority)
1099 if (ges_timeline_object_set_priority_internal (object, priority))
1100 #if GLIB_CHECK_VERSION(2,26,0)
1101 g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_PRIORITY]);
1103 g_object_notify (G_OBJECT (object), "priority");
1108 * ges_timeline_object_find_track_object:
1109 * @object: a #GESTimelineObject
1110 * @track: a #GESTrack or NULL
1111 * @type: a #GType indicating the type of track object you are looking
1112 * for or %G_TYPE_NONE if you do not care about the track type.
1114 * Finds the #GESTrackObject controlled by @object that is used in @track. You
1115 * may optionally specify a GType to further narrow search criteria.
1117 * Note: If many objects match, then the one with the highest priority will be
1120 * Returns: (transfer full): The #GESTrackObject used by @track, else %NULL.
1121 * Unref after usage.
1125 ges_timeline_object_find_track_object (GESTimelineObject * object,
1126 GESTrack * track, GType type)
1128 GESTrackObject *ret = NULL;
1130 GESTrackObject *otmp;
1132 g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), NULL);
1133 g_return_val_if_fail (GES_IS_TRACK (track), NULL);
1135 for (tmp = object->priv->trackobjects; tmp; tmp = g_list_next (tmp)) {
1136 otmp = (GESTrackObject *) tmp->data;
1138 if (ges_track_object_get_track (otmp) == track) {
1139 if ((type != G_TYPE_NONE) &&
1140 !G_TYPE_CHECK_INSTANCE_TYPE (tmp->data, type))
1143 ret = GES_TRACK_OBJECT (tmp->data);
1153 * ges_timeline_object_get_layer:
1154 * @object: a #GESTimelineObject
1156 * Get the #GESTimelineLayer to which this object belongs.
1158 * Returns: (transfer full): The #GESTimelineLayer where this @object is being
1159 * used, or %NULL if it is not used on any layer. The caller should unref it
1163 ges_timeline_object_get_layer (GESTimelineObject * object)
1165 g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), NULL);
1167 if (object->priv->layer != NULL)
1168 g_object_ref (G_OBJECT (object->priv->layer));
1170 return object->priv->layer;
1174 * ges_timeline_object_get_track_objects:
1175 * @object: a #GESTimelineObject
1177 * Get the list of #GESTrackObject contained in @object
1179 * Returns: (transfer full) (element-type GESTrackObject): The list of
1180 * trackobject contained in @object.
1181 * The user is responsible for unreffing the contained objects
1182 * and freeing the list.
1185 ges_timeline_object_get_track_objects (GESTimelineObject * object)
1190 g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), NULL);
1192 ret = g_list_copy (object->priv->trackobjects);
1194 for (tmp = ret; tmp; tmp = tmp->next) {
1195 g_object_ref (tmp->data);
1202 sort_track_effects (gpointer a, gpointer b, GESTimelineObject * object)
1204 guint prio_offset_a, prio_offset_b;
1205 ObjectMapping *map_a, *map_b;
1206 GESTrackObject *obj_a, *obj_b;
1208 obj_a = GES_TRACK_OBJECT (a);
1209 obj_b = GES_TRACK_OBJECT (b);
1211 map_a = find_object_mapping (object, obj_a);
1212 map_b = find_object_mapping (object, obj_b);
1214 prio_offset_a = map_a->priority_offset;
1215 prio_offset_b = map_b->priority_offset;
1217 if ((gint) prio_offset_a > (guint) prio_offset_b)
1219 if ((guint) prio_offset_a < (guint) prio_offset_b)
1226 * ges_timeline_object_get_top_effects:
1227 * @object: The origin #GESTimelineObject
1229 * Get effects applied on @object
1231 * Returns: (transfer full) (element-type GESTrackObject): a #GList of the
1232 * #GESTrackEffect that are applied on @object order by ascendant priorities.
1233 * The refcount of the objects will be increased. The user will have to
1234 * unref each #GESTrackEffect and free the #GList.
1239 ges_timeline_object_get_top_effects (GESTimelineObject * object)
1244 g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), NULL);
1246 GST_DEBUG_OBJECT (object, "Getting the %i top effects",
1247 object->priv->nb_effects);
1250 for (tmp = object->priv->trackobjects, i = 0; i < object->priv->nb_effects;
1251 tmp = tmp->next, i++) {
1252 ret = g_list_append (ret, g_object_ref (tmp->data));
1259 * ges_timeline_object_get_top_effect_position:
1260 * @object: The origin #GESTimelineObject
1261 * @effect: The #GESTrackEffect we want to get the top position from
1263 * Gets the top position of an effect.
1265 * Returns: The top position of the effect, -1 if something went wrong.
1270 ges_timeline_object_get_top_effect_position (GESTimelineObject * object,
1271 GESTrackEffect * effect)
1273 g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), -1);
1275 return find_object_mapping (object,
1276 GES_TRACK_OBJECT (effect))->priority_offset;
1280 * ges_timeline_object_set_top_effect_priority:
1281 * @object: The origin #GESTimelineObject
1282 * @effect: The #GESTrackEffect to move
1283 * @newpriority: the new position at which to move the @effect inside this
1284 * #GESTimelineObject
1286 * This is a convenience method that lets you set the priority of a top effect.
1288 * Returns: %TRUE if @effect was successfuly moved, %FALSE otherwise.
1293 ges_timeline_object_set_top_effect_priority (GESTimelineObject * object,
1294 GESTrackEffect * effect, guint newpriority)
1299 GESTrackObject *tck_obj;
1300 GESTimelineObjectPrivate *priv;
1302 g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
1304 tck_obj = GES_TRACK_OBJECT (effect);
1305 priv = object->priv;
1306 current_prio = ges_track_object_get_priority (tck_obj);
1308 /* We don't change the priority */
1309 if (current_prio == newpriority ||
1310 (G_UNLIKELY (ges_track_object_get_timeline_object (tck_obj) != object)))
1313 if (newpriority > (object->priv->nb_effects - 1)) {
1314 GST_DEBUG ("You are trying to make %p not a top effect", effect);
1318 if (current_prio > object->priv->nb_effects) {
1319 GST_DEBUG ("%p is not a top effect");
1323 if (tck_obj->priority < newpriority)
1328 ges_track_object_set_priority (tck_obj, newpriority);
1329 for (tmp = priv->trackobjects; tmp; tmp = tmp->next) {
1330 GESTrackObject *tmpo = GES_TRACK_OBJECT (tmp->data);
1331 guint tck_priority = ges_track_object_get_priority (tmpo);
1333 if ((inc == +1 && tck_priority >= newpriority) ||
1334 (inc == -1 && tck_priority <= newpriority)) {
1335 ges_track_object_set_priority (tmpo, tck_priority + inc);
1339 priv->trackobjects = g_list_sort_with_data (priv->trackobjects,
1340 (GCompareDataFunc) sort_track_effects, object);
1346 * ges_timeline_object_edit:
1347 * @object: the #GESTimelineObject to edit
1348 * @layers: (element-type GESTimelineLayer): The layers you want the edit to
1349 * happen in, %NULL means that the edition is done in all the
1350 * #GESTimelineLayers contained in the current timeline.
1351 * @new_layer_priority: The priority of the layer @object should land in.
1352 * If the layer you're trying to move the object to doesn't exist, it will
1353 * be created automatically. -1 means no move.
1354 * @mode: The #GESEditMode in which the editition will happen.
1355 * @edge: The #GESEdge the edit should happen on.
1356 * @position: The position at which to edit @object (in nanosecond)
1358 * Edit @object in the different exisiting #GESEditMode modes. In the case of
1359 * slide, and roll, you need to specify a #GESEdge
1361 * Returns: %TRUE if the object as been edited properly, %FALSE if an error
1367 ges_timeline_object_edit (GESTimelineObject * object, GList * layers,
1368 gint new_layer_priority, GESEditMode mode, GESEdge edge, guint64 position)
1371 gboolean ret = TRUE;
1372 GESTimelineLayer *layer;
1374 g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
1376 if (!G_UNLIKELY (object->priv->trackobjects)) {
1377 GST_WARNING_OBJECT (object, "Trying to edit, but not containing"
1378 "any TrackObject yet.");
1380 } else if (position < 0) {
1381 GST_DEBUG_OBJECT (object, "Trying to move before 0, not moving");
1384 for (tmp = object->priv->trackobjects; tmp; tmp = g_list_next (tmp)) {
1385 if (ges_track_object_is_locked (tmp->data)) {
1386 ret &= ges_track_object_edit (tmp->data, layers, mode, edge, position);
1391 /* Moving to layer */
1392 if (new_layer_priority == -1) {
1393 GST_DEBUG_OBJECT (object, "Not moving new prio %d", new_layer_priority);
1395 gint priority_offset;
1397 layer = object->priv->layer;
1398 if (layer == NULL) {
1399 GST_WARNING_OBJECT (object, "Not in any layer yet, not moving");
1403 priority_offset = new_layer_priority -
1404 ges_timeline_layer_get_priority (layer);
1406 ret &= timeline_context_to_layer (layer->timeline, priority_offset);
1413 * ges_timeline_object_split:
1414 * @object: the #GESTimelineObject to split
1415 * @position: a #GstClockTime representing the position at which to split
1418 * The function modifies @object, and creates another #GESTimelineObject so
1419 * we have two clips at the end, splitted at the time specified by @position.
1421 * Returns: (transfer full): The newly created #GESTimelineObject resulting from
1427 ges_timeline_object_split (GESTimelineObject * object, guint64 position)
1431 GESTimelineObject *new_object;
1432 GESTimelineObjectPrivate *priv;
1434 GstClockTime start, inpoint, duration;
1436 g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), NULL);
1437 g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (position), NULL);
1439 priv = object->priv;
1441 duration = GES_TIMELINE_OBJECT_DURATION (object);
1442 start = GES_TIMELINE_OBJECT_START (object);
1443 inpoint = GES_TIMELINE_OBJECT_INPOINT (object);
1445 if (position >= start + duration || position <= start) {
1446 GST_WARNING_OBJECT (object, "Can not split %" GST_TIME_FORMAT
1447 " out of boundaries", GST_TIME_ARGS (position));
1451 GST_DEBUG_OBJECT (object, "Spliting at %" GST_TIME_FORMAT,
1452 GST_TIME_ARGS (position));
1454 /* Create the new TimelineObject */
1455 new_object = ges_timeline_object_copy (object, FALSE);
1457 /* Set new timing properties on the TimelineObject */
1458 ges_timeline_object_set_start (new_object, position);
1459 ges_timeline_object_set_inpoint (new_object, object->inpoint +
1460 duration - (duration + start - position));
1461 ges_timeline_object_set_duration (new_object, duration + start - position);
1463 if (object->priv->layer) {
1464 /* We do not want the timeline to create again TrackObject-s */
1465 ges_timeline_object_set_moving_from_layer (new_object, TRUE);
1466 ges_timeline_layer_add_object (object->priv->layer, new_object);
1467 ges_timeline_object_set_moving_from_layer (new_object, FALSE);
1470 /* We first set the new duration and the child mapping will be updated
1471 * properly in the following loop */
1472 object->duration = position - object->start;
1473 for (tmp = priv->trackobjects; tmp; tmp = tmp->next) {
1476 GESTrackObject *new_tckobj, *tckobj = GES_TRACK_OBJECT (tmp->data);
1478 duration = ges_track_object_get_duration (tckobj);
1479 start = ges_track_object_get_start (tckobj);
1480 inpoint = ges_track_object_get_inpoint (tckobj);
1482 if (position <= start || position >= (start + duration)) {
1483 GST_DEBUG_OBJECT (tckobj, "Outside %" GST_TIME_FORMAT "the boundaries "
1484 "not copying it ( start %" GST_TIME_FORMAT ", end %" GST_TIME_FORMAT
1485 ")", GST_TIME_ARGS (position), GST_TIME_ARGS (tckobj->start),
1486 GST_TIME_ARGS (tckobj->start + tckobj->duration));
1490 new_tckobj = ges_track_object_copy (tckobj, TRUE);
1491 if (new_tckobj == NULL) {
1492 GST_WARNING_OBJECT (tckobj, "Could not create a copy");
1496 ges_timeline_object_add_track_object (new_object, new_tckobj);
1498 track = ges_track_object_get_track (tckobj);
1500 GST_DEBUG_OBJECT (tckobj, "Was not in a track, not adding %p to"
1501 "any track", new_tckobj);
1503 ges_track_add_object (track, new_tckobj);
1505 /* Unlock TrackObject-s as we do not want the container to move
1507 locked = ges_track_object_is_locked (tckobj);
1508 ges_track_object_set_locked (new_tckobj, FALSE);
1509 ges_track_object_set_locked (tckobj, FALSE);
1511 /* Set 'new' track object timing propeties */
1512 ges_track_object_set_start (new_tckobj, position);
1513 ges_track_object_set_inpoint (new_tckobj, inpoint + duration - (duration +
1515 ges_track_object_set_duration (new_tckobj, duration + start - position);
1517 /* Set 'old' track object duration */
1518 ges_track_object_set_duration (tckobj, position - start);
1520 /* And let track objects in the same locking state as before. */
1521 ges_track_object_set_locked (tckobj, locked);
1522 ges_track_object_set_locked (new_tckobj, locked);
1528 /* TODO implement the deep parameter, and make it public */
1529 static GESTimelineObject *
1530 ges_timeline_object_copy (GESTimelineObject * object, gboolean * deep)
1532 GESTimelineObject *ret = NULL;
1535 guint n, n_specs, n_params;
1537 g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), NULL);
1540 g_object_class_list_properties (G_OBJECT_GET_CLASS (object), &n_specs);
1541 params = g_new0 (GParameter, n_specs);
1544 for (n = 0; n < n_specs; ++n) {
1545 if (strcmp (specs[n]->name, "parent") &&
1546 (specs[n]->flags & G_PARAM_READWRITE) == G_PARAM_READWRITE) {
1547 params[n_params].name = g_intern_string (specs[n]->name);
1548 g_value_init (¶ms[n_params].value, specs[n]->value_type);
1549 g_object_get_property (G_OBJECT (object), specs[n]->name,
1550 ¶ms[n_params].value);
1555 ret = g_object_newv (G_TYPE_FROM_INSTANCE (object), n_params, params);
1564 * ges_timeline_object_set_supported_formats:
1565 * @object: the #GESTimelineObject to set supported formats on
1566 * @supportedformats: the #GESTrackType defining formats supported by @object
1568 * Sets the formats supported by the file.
1573 ges_timeline_object_set_supported_formats (GESTimelineObject * object,
1574 GESTrackType supportedformats)
1576 g_return_if_fail (GES_IS_TIMELINE_OBJECT (object));
1578 object->priv->supportedformats = supportedformats;
1582 * ges_timeline_object_get_supported_formats:
1583 * @object: the #GESTimelineObject
1585 * Get the formats supported by @object.
1587 * Returns: The formats supported by @object.
1592 ges_timeline_object_get_supported_formats (GESTimelineObject * object)
1594 g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object),
1595 GES_TRACK_TYPE_UNKNOWN);
1597 return object->priv->supportedformats;
1601 * ges_timeline_object_objects_set_locked:
1602 * @object: the #GESTimelineObject
1603 * @locked: whether the #GESTrackObject contained in @object are locked to it.
1605 * Set the locking status of all the #GESTrackObject contained in @object to @locked.
1606 * See the ges_track_object_set_locked documentation for more details.
1611 ges_timeline_object_objects_set_locked (GESTimelineObject * object,
1616 g_return_if_fail (GES_IS_TIMELINE_OBJECT (object));
1618 for (tmp = object->priv->mappings; tmp; tmp = g_list_next (tmp)) {
1619 ges_track_object_set_locked (((ObjectMapping *) tmp->data)->object, locked);
1624 * ges_timeline_object_get_max_duration:
1625 * @object: The #GESTimelineObject to retrieve max duration from
1627 * Get the max duration of @object.
1629 * Returns: The max duration of @object
1634 ges_timeline_object_get_max_duration (GESTimelineObject * object)
1636 g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), 0);
1638 return object->priv->maxduration;
1642 * ges_timeline_object_set_max_duration:
1643 * @object: The #GESTimelineObject to retrieve max duration from
1644 * @maxduration: The maximum duration of @object
1646 * Returns: Set the max duration of @object
1651 ges_timeline_object_set_max_duration (GESTimelineObject * object,
1652 guint64 maxduration)
1654 GESTimelineObjectClass *klass = GES_TIMELINE_OBJECT_GET_CLASS (object);
1656 g_return_if_fail (GES_IS_TIMELINE_OBJECT (object));
1658 object->priv->maxduration = maxduration;
1659 klass->set_max_duration (object, maxduration);
1663 * ges_timeline_object_ripple:
1664 * @object: The #GESTimeline to ripple.
1665 * @start: The new start of @object in ripple mode.
1667 * Edits @object in ripple mode. It allows you to modify the
1668 * start of @object and move the following neighbours accordingly.
1669 * This will change the overall timeline duration.
1671 * You could also use:
1673 * #ges_timeline_object_edit (@object, @layers,
1674 * new_layer_priority=-1, GES_EDIT_MODE_RIPPLE, GES_EDGE_NONE,
1677 * Which lets you more control over layer management.
1679 * Returns: %TRUE if the object as been rippled properly, %FALSE if an error
1683 ges_timeline_object_ripple (GESTimelineObject * object, guint64 start)
1685 GList *tmp, *tckobjs;
1686 gboolean ret = TRUE;
1687 GESTimeline *timeline;
1689 g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
1691 timeline = ges_timeline_layer_get_timeline (object->priv->layer);
1693 if (timeline == NULL) {
1694 GST_DEBUG ("Not in a timeline yet");
1698 tckobjs = ges_timeline_object_get_track_objects (object);
1699 for (tmp = tckobjs; tmp; tmp = g_list_next (tmp)) {
1700 if (ges_track_object_is_locked (tmp->data)) {
1701 ret = timeline_ripple_object (timeline, GES_TRACK_OBJECT (tmp->data),
1702 NULL, GES_EDGE_NONE, start);
1703 /* As we work only with locked objects, the changes will be reflected
1704 * to others controlled TrackObjects */
1708 g_list_free_full (tckobjs, g_object_unref);
1714 * ges_timeline_object_ripple_end:
1715 * @object: The #GESTimeline to ripple.
1716 * @end: The new end (start + duration) of @object in ripple mode. It will
1717 * basically only change the duration of @object.
1719 * Edits @object in ripple mode. It allows you to modify the
1720 * duration of a @object and move the following neighbours accordingly.
1721 * This will change the overall timeline duration.
1723 * You could also use:
1725 * #ges_timeline_object_edit (@object, @layers,
1726 * new_layer_priority=-1, GES_EDIT_MODE_RIPPLE, GES_EDGE_END, @end);
1728 * Which lets you more control over layer management.
1730 * Returns: %TRUE if the object as been rippled properly, %FALSE if an error
1734 ges_timeline_object_ripple_end (GESTimelineObject * object, guint64 end)
1736 GList *tmp, *tckobjs;
1737 gboolean ret = TRUE;
1738 GESTimeline *timeline;
1740 g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
1742 timeline = ges_timeline_layer_get_timeline (object->priv->layer);
1744 if (timeline == NULL) {
1745 GST_DEBUG ("Not in a timeline yet");
1749 tckobjs = ges_timeline_object_get_track_objects (object);
1750 for (tmp = tckobjs; tmp; tmp = g_list_next (tmp)) {
1751 if (ges_track_object_is_locked (tmp->data)) {
1752 ret = timeline_ripple_object (timeline, GES_TRACK_OBJECT (tmp->data),
1753 NULL, GES_EDGE_END, end);
1754 /* As we work only with locked objects, the changes will be reflected
1755 * to others controlled TrackObjects */
1759 g_list_free_full (tckobjs, g_object_unref);
1765 * ges_timeline_object_roll_start:
1766 * @start: The new start of @object in roll mode, it will also adapat
1767 * the in-point of @object according
1769 * Edits @object in roll mode. It allows you to modify the
1770 * start and inpoint of a @object and "resize" (basicly change the duration
1771 * in this case) of the previous neighbours accordingly.
1772 * This will not change the overall timeline duration.
1774 * You could also use:
1776 * #ges_timeline_object_edit (@object, @layers,
1777 * new_layer_priority=-1, GES_EDIT_MODE_ROLL, GES_EDGE_START, @start);
1779 * Which lets you more control over layer management.
1781 * Returns: %TRUE if the object as been roll properly, %FALSE if an error
1785 ges_timeline_object_roll_start (GESTimelineObject * object, guint64 start)
1787 GList *tmp, *tckobjs;
1788 gboolean ret = TRUE;
1789 GESTimeline *timeline;
1791 g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
1793 timeline = ges_timeline_layer_get_timeline (object->priv->layer);
1795 if (timeline == NULL) {
1796 GST_DEBUG ("Not in a timeline yet");
1800 tckobjs = ges_timeline_object_get_track_objects (object);
1801 for (tmp = tckobjs; tmp; tmp = g_list_next (tmp)) {
1802 if (ges_track_object_is_locked (tmp->data)) {
1803 ret = timeline_roll_object (timeline, GES_TRACK_OBJECT (tmp->data),
1804 NULL, GES_EDGE_START, start);
1805 /* As we work only with locked objects, the changes will be reflected
1806 * to others controlled TrackObjects */
1810 g_list_free_full (tckobjs, g_object_unref);
1816 * ges_timeline_object_roll_end:
1817 * @object: The #GESTimeline to roll.
1818 * @end: The new end (start + duration) of @object in roll mode
1820 * Edits @object in roll mode. It allows you to modify the
1821 * duration of a @object and trim (basicly change the start + inpoint
1822 * in this case) the following neighbours accordingly.
1823 * This will not change the overall timeline duration.
1825 * You could also use:
1827 * #ges_timeline_object_edit (@object, @layers,
1828 * new_layer_priority=-1, GES_EDIT_MODE_ROLL, GES_EDGE_END, @end);
1830 * Which lets you more control over layer management.
1832 * Returns: %TRUE if the object as been rolled properly, %FALSE if an error
1836 ges_timeline_object_roll_end (GESTimelineObject * object, guint64 end)
1838 GList *tmp, *tckobjs;
1839 gboolean ret = TRUE;
1840 GESTimeline *timeline;
1842 g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
1844 timeline = ges_timeline_layer_get_timeline (object->priv->layer);
1846 if (timeline == NULL) {
1847 GST_DEBUG ("Not in a timeline yet");
1852 tckobjs = ges_timeline_object_get_track_objects (object);
1853 for (tmp = tckobjs; tmp; tmp = g_list_next (tmp)) {
1854 if (ges_track_object_is_locked (tmp->data)) {
1855 ret = timeline_roll_object (timeline, GES_TRACK_OBJECT (tmp->data),
1856 NULL, GES_EDGE_END, end);
1857 /* As we work only with locked objects, the changes will be reflected
1858 * to others controlled TrackObjects */
1862 g_list_free_full (tckobjs, g_object_unref);
1868 * ges_timeline_object_trim_start:
1869 * @object: The #GESTimeline to trim.
1870 * @start: The new start of @object in trim mode, will adapt the inpoint
1871 * of @object accordingly
1873 * Edits @object in trim mode. It allows you to modify the
1874 * inpoint and start of @object.
1875 * This will not change the overall timeline duration.
1877 * You could also use:
1879 * #ges_timeline_object_edit (@object, @layers,
1880 * new_layer_priority=-1, GES_EDIT_MODE_TRIM, GES_EDGE_START, @start);
1882 * Which lets you more control over layer management.
1884 * Note that to trim the end of an object you can just set its duration. The same way
1885 * as this method, it will take into account the snapping-distance property of the
1886 * timeline in which @object is.
1888 * Returns: %TRUE if the object as been trimmed properly, %FALSE if an error
1892 ges_timeline_object_trim_start (GESTimelineObject * object, guint64 start)
1894 GList *tmp, *tckobjs;
1895 gboolean ret = TRUE;
1896 GESTimeline *timeline;
1898 g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
1900 timeline = ges_timeline_layer_get_timeline (object->priv->layer);
1902 if (timeline == NULL) {
1903 GST_DEBUG ("Not in a timeline yet");
1907 tckobjs = ges_timeline_object_get_track_objects (object);
1908 for (tmp = tckobjs; tmp; tmp = g_list_next (tmp)) {
1909 if (ges_track_object_is_locked (tmp->data)) {
1910 ret = timeline_trim_object (timeline, GES_TRACK_OBJECT (tmp->data),
1911 NULL, GES_EDGE_START, start);
1915 g_list_free_full (tckobjs, g_object_unref);
1921 update_height (GESTimelineObject * object)
1924 guint32 min_prio = G_MAXUINT32, max_prio = 0;
1926 /* Go over all childs and check if height has changed */
1927 for (tmp = object->priv->trackobjects; tmp; tmp = tmp->next) {
1928 guint tck_priority =
1929 ges_track_object_get_priority (GES_TRACK_OBJECT (tmp->data));
1931 if (tck_priority < min_prio)
1932 min_prio = tck_priority;
1933 if (tck_priority > max_prio)
1934 max_prio = tck_priority;
1937 /* FIXME : We only grow the height */
1938 if (object->height < (max_prio - min_prio + 1)) {
1939 object->height = max_prio - min_prio + 1;
1940 GST_DEBUG ("Updating height %i", object->height);
1941 #if GLIB_CHECK_VERSION(2,26,0)
1942 g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_HEIGHT]);
1944 g_object_notify (G_OBJECT (object), "height");
1950 * PROPERTY NOTIFICATIONS FROM TRACK OBJECTS
1954 track_object_start_changed_cb (GESTrackObject * child,
1955 GParamSpec * arg G_GNUC_UNUSED, GESTimelineObject * object)
1959 if (object->priv->ignore_notifies)
1962 map = find_object_mapping (object, child);
1963 if (G_UNLIKELY (map == NULL))
1964 /* something massively screwed up if we get this */
1967 if (!ges_track_object_is_locked (child)) {
1968 /* Update the internal start_offset */
1969 map->start_offset = object->start - child->start;
1971 /* Or update the parent start */
1972 object->priv->initiated_move = child;
1973 ges_timeline_object_set_start (object, child->start + map->start_offset);
1974 object->priv->initiated_move = NULL;
1979 track_object_inpoint_changed_cb (GESTrackObject * child,
1980 GParamSpec * arg G_GNUC_UNUSED, GESTimelineObject * object)
1984 if (object->priv->ignore_notifies)
1987 map = find_object_mapping (object, child);
1988 if (G_UNLIKELY (map == NULL))
1989 /* something massively screwed up if we get this */
1992 if (!ges_track_object_is_locked (child)) {
1993 /* Update the internal start_offset */
1994 map->inpoint_offset = object->inpoint - child->inpoint;
1996 /* Or update the parent start */
1997 object->priv->initiated_move = child;
1998 ges_timeline_object_set_inpoint (object,
1999 child->inpoint + map->inpoint_offset);
2000 object->priv->initiated_move = NULL;
2006 track_object_duration_changed_cb (GESTrackObject * child,
2007 GParamSpec * arg G_GNUC_UNUSED, GESTimelineObject * object)
2011 if (object->priv->ignore_notifies)
2014 map = find_object_mapping (object, child);
2015 if (G_UNLIKELY (map == NULL))
2016 /* something massively screwed up if we get this */
2019 if (!ges_track_object_is_locked (child)) {
2020 /* Update the internal start_offset */
2021 map->duration_offset = object->duration - child->duration;
2023 /* Or update the parent start */
2024 object->priv->initiated_move = child;
2025 ges_timeline_object_set_duration (object,
2026 child->duration + map->duration_offset);
2027 object->priv->initiated_move = NULL;
2033 track_object_priority_changed_cb (GESTrackObject * child,
2034 GParamSpec * arg G_GNUC_UNUSED, GESTimelineObject * object)
2037 guint32 layer_min_gnl_prio, layer_max_gnl_prio;
2039 guint tck_priority = ges_track_object_get_priority (child);
2041 GST_DEBUG ("TrackObject %p priority changed to %i", child,
2042 ges_track_object_get_priority (child));
2044 if (object->priv->ignore_notifies)
2047 update_height (object);
2048 map = find_object_mapping (object, child);
2049 get_layer_priorities (object->priv->layer, &layer_min_gnl_prio,
2050 &layer_max_gnl_prio);
2052 if (G_UNLIKELY (map == NULL))
2053 /* something massively screwed up if we get this */
2056 if (!ges_track_object_is_locked (child)) {
2057 if (tck_priority < layer_min_gnl_prio || tck_priority > layer_max_gnl_prio) {
2058 GST_WARNING ("%p priority of %i, is outside of its containing "
2059 "layer space. (%d/%d). This is a bug in the program.", object,
2060 tck_priority, layer_min_gnl_prio, layer_max_gnl_prio);
2063 /* Update the internal priority_offset */
2064 map->priority_offset =
2065 tck_priority - (layer_min_gnl_prio + object->priority);
2067 } else if (tck_priority < layer_min_gnl_prio + object->priority) {
2068 /* Or update the parent priority, the object priority is always the
2069 * highest priority (smaller number) */
2070 if (tck_priority < layer_min_gnl_prio || layer_max_gnl_prio < tck_priority) {
2072 GST_WARNING ("%p priority of %i, is outside of its containing "
2073 "layer space. (%d/%d). This is a bug in the program.", object,
2074 tck_priority, layer_min_gnl_prio, layer_max_gnl_prio);
2078 ges_timeline_object_set_priority (object,
2079 tck_priority - layer_min_gnl_prio);
2082 GST_DEBUG ("object %p priority %d child %p priority %d", object,
2083 object->priority, child, ges_track_object_get_priority (child));
2087 get_layer_priorities (GESTimelineLayer * layer, guint32 * layer_min_gnl_prio,
2088 guint32 * layer_max_gnl_prio)
2091 *layer_min_gnl_prio = layer->min_gnl_priority;
2092 *layer_max_gnl_prio = layer->max_gnl_priority;
2094 *layer_min_gnl_prio = 0;
2095 *layer_max_gnl_prio = G_MAXUINT32;