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-track-object
23 * @short_description: Base Class for objects contained in a #GESTrack
25 * #GESTrackObject is the Base Class for any object that can be contained in a
28 * It contains the basic information as to the location of the object within
29 * its container, like the start position, the in-point, the duration and the
33 #include "ges-internal.h"
34 #include "gesmarshal.h"
35 #include "ges-track-object.h"
36 #include "ges-timeline-object.h"
37 #include <gobject/gvaluecollector.h>
39 G_DEFINE_ABSTRACT_TYPE (GESTrackObject, ges_track_object,
40 G_TYPE_INITIALLY_UNOWNED);
42 struct _GESTrackObjectPrivate
44 /* These fields are only used before the gnlobject is available */
45 guint64 pending_start;
46 guint64 pending_inpoint;
47 guint64 pending_duration;
48 guint32 pending_priority;
49 gboolean pending_active;
51 GstElement *gnlobject; /* The GnlObject */
52 GstElement *element; /* The element contained in the gnlobject (can be NULL) */
54 /* We keep a link between properties name and elements internally
55 * The hashtable should look like
56 * {GParamaSpec ---> element,}*/
57 GHashTable *properties_hashtable;
59 GESTimelineObject *timelineobj;
66 gboolean locked; /* If TRUE, then moves in sync with its controlling
67 * GESTimelineObject */
83 static GParamSpec *properties[PROP_LAST];
91 static guint ges_track_object_signals[LAST_SIGNAL] = { 0 };
93 static GstElement *ges_track_object_create_gnl_object_func (GESTrackObject *
96 static void gnlobject_start_cb (GstElement * gnlobject, GParamSpec * arg
97 G_GNUC_UNUSED, GESTrackObject * obj);
99 static void gnlobject_media_start_cb (GstElement * gnlobject, GParamSpec * arg
100 G_GNUC_UNUSED, GESTrackObject * obj);
102 static void gnlobject_priority_cb (GstElement * gnlobject, GParamSpec * arg
103 G_GNUC_UNUSED, GESTrackObject * obj);
105 static void gnlobject_duration_cb (GstElement * gnlobject, GParamSpec * arg
106 G_GNUC_UNUSED, GESTrackObject * obj);
108 static void gnlobject_active_cb (GstElement * gnlobject, GParamSpec * arg
109 G_GNUC_UNUSED, GESTrackObject * obj);
111 static void connect_properties_signals (GESTrackObject * object);
112 static void connect_signal (gpointer key, gpointer value, gpointer user_data);
113 static void gst_element_prop_changed_cb (GstElement * element, GParamSpec * arg
114 G_GNUC_UNUSED, GESTrackObject * obj);
116 static inline gboolean
117 ges_track_object_set_start_internal (GESTrackObject * object, guint64 start);
118 static inline gboolean
119 ges_track_object_set_inpoint_internal (GESTrackObject * object,
121 static inline gboolean ges_track_object_set_duration_internal (GESTrackObject *
122 object, guint64 duration);
123 static inline gboolean ges_track_object_set_priority_internal (GESTrackObject *
124 object, guint32 priority);
126 ges_track_object_set_locked_internal (GESTrackObject * object, gboolean locked);
128 static GParamSpec **default_list_children_properties (GESTrackObject * object,
129 guint * n_properties);
132 ges_track_object_get_property (GObject * object, guint property_id,
133 GValue * value, GParamSpec * pspec)
135 GESTrackObject *tobj = GES_TRACK_OBJECT (object);
137 switch (property_id) {
139 g_value_set_uint64 (value, ges_track_object_get_start (tobj));
142 g_value_set_uint64 (value, ges_track_object_get_inpoint (tobj));
145 g_value_set_uint64 (value, ges_track_object_get_duration (tobj));
148 g_value_set_uint (value, ges_track_object_get_priority (tobj));
151 g_value_set_boolean (value, ges_track_object_is_active (tobj));
154 g_value_set_boolean (value, ges_track_object_is_locked (tobj));
156 case PROP_MAX_DURATION:
157 g_value_set_uint64 (value, tobj->priv->maxduration);
160 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
165 ges_track_object_set_property (GObject * object, guint property_id,
166 const GValue * value, GParamSpec * pspec)
168 GESTrackObject *tobj = GES_TRACK_OBJECT (object);
170 switch (property_id) {
172 ges_track_object_set_start_internal (tobj, g_value_get_uint64 (value));
175 ges_track_object_set_inpoint_internal (tobj, g_value_get_uint64 (value));
178 ges_track_object_set_duration_internal (tobj, g_value_get_uint64 (value));
181 ges_track_object_set_priority_internal (tobj, g_value_get_uint (value));
184 ges_track_object_set_active (tobj, g_value_get_boolean (value));
187 ges_track_object_set_locked_internal (tobj, g_value_get_boolean (value));
189 case PROP_MAX_DURATION:
190 ges_track_object_set_max_duration (tobj, g_value_get_uint64 (value));
193 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
198 ges_track_object_dispose (GObject * object)
200 GESTrackObjectPrivate *priv = GES_TRACK_OBJECT (object)->priv;
201 if (priv->properties_hashtable)
202 g_hash_table_destroy (priv->properties_hashtable);
204 G_OBJECT_CLASS (ges_track_object_parent_class)->dispose (object);
208 ges_track_object_finalize (GObject * object)
210 G_OBJECT_CLASS (ges_track_object_parent_class)->finalize (object);
214 ges_track_object_class_init (GESTrackObjectClass * klass)
216 GObjectClass *object_class = G_OBJECT_CLASS (klass);
218 g_type_class_add_private (klass, sizeof (GESTrackObjectPrivate));
220 object_class->get_property = ges_track_object_get_property;
221 object_class->set_property = ges_track_object_set_property;
222 object_class->dispose = ges_track_object_dispose;
223 object_class->finalize = ges_track_object_finalize;
226 * GESTrackObject:start
228 * The position of the object in the container #GESTrack (in nanoseconds).
230 properties[PROP_START] = g_param_spec_uint64 ("start", "Start",
231 "The position in the container", 0, G_MAXUINT64, 0, G_PARAM_READWRITE);
232 g_object_class_install_property (object_class, PROP_START,
233 properties[PROP_START]);
236 * GESTrackObject:in-point
238 * The in-point at which this #GESTrackObject will start outputting data
239 * from its contents (in nanoseconds).
241 * Ex: an in-point of 5 seconds means that the first outputted buffer will
242 * be the one located 5 seconds in the controlled resource.
244 properties[PROP_INPOINT] =
245 g_param_spec_uint64 ("in-point", "In-point", "The in-point", 0,
246 G_MAXUINT64, 0, G_PARAM_READWRITE);
247 g_object_class_install_property (object_class, PROP_INPOINT,
248 properties[PROP_INPOINT]);
251 * GESTrackObject:duration
253 * The duration (in nanoseconds) which will be used in the container #GESTrack
254 * starting from 'in-point'.
257 properties[PROP_DURATION] =
258 g_param_spec_uint64 ("duration", "Duration", "The duration to use", 0,
259 G_MAXUINT64, GST_SECOND, G_PARAM_READWRITE);
260 g_object_class_install_property (object_class, PROP_DURATION,
261 properties[PROP_DURATION]);
264 * GESTrackObject:priority
266 * The priority of the object within the containing #GESTrack.
267 * If two objects intersect over the same region of time, the @priority
268 * property is used to decide which one takes precedence.
270 * The highest priority (that supercedes everything) is 0, and then lowering
271 * priorities go in increasing numerical value (with #G_MAXUINT64 being the
274 properties[PROP_PRIORITY] = g_param_spec_uint ("priority", "Priority",
275 "The priority of the object", 0, G_MAXUINT, 0, G_PARAM_READWRITE);
276 g_object_class_install_property (object_class, PROP_PRIORITY,
277 properties[PROP_PRIORITY]);
280 * GESTrackObject:active
282 * Whether the object should be taken into account in the #GESTrack output.
283 * If #FALSE, then its contents will not be used in the resulting track.
285 properties[PROP_ACTIVE] =
286 g_param_spec_boolean ("active", "Active", "Use object in output", TRUE,
288 g_object_class_install_property (object_class, PROP_ACTIVE,
289 properties[PROP_ACTIVE]);
292 * GESTrackObject:locked
294 * If %TRUE, then moves in sync with its controlling #GESTimelineObject
296 properties[PROP_LOCKED] =
297 g_param_spec_boolean ("locked", "Locked",
298 "Moves in sync with its controling TimelineObject", TRUE,
300 g_object_class_install_property (object_class, PROP_LOCKED,
301 properties[PROP_LOCKED]);
304 * GESTrackObject:max-duration:
306 * The maximum duration (in nanoseconds) of the #GESTrackObject.
310 g_object_class_install_property (object_class, PROP_MAX_DURATION,
311 g_param_spec_uint64 ("max-duration", "Maximum duration",
312 "The duration of the object", GST_CLOCK_TIME_NONE, G_MAXUINT64,
313 G_MAXUINT64, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
316 * GESTrackObject::deep-notify:
317 * @track_object: a #GESTrackObject
318 * @prop_object: the object that originated the signal
319 * @prop: the property that changed
321 * The deep notify signal is used to be notified of property changes of all
322 * the childs of @track_object
326 ges_track_object_signals[DEEP_NOTIFY] =
327 g_signal_new ("deep-notify", G_TYPE_FROM_CLASS (klass),
328 G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED |
329 G_SIGNAL_NO_HOOKS, 0, NULL, NULL, gst_marshal_VOID__OBJECT_PARAM,
330 G_TYPE_NONE, 2, GST_TYPE_ELEMENT, G_TYPE_PARAM);
332 klass->create_gnl_object = ges_track_object_create_gnl_object_func;
333 /* There is no 'get_props_hashtable' default implementation */
334 klass->get_props_hastable = NULL;
335 klass->list_children_properties = default_list_children_properties;
339 ges_track_object_init (GESTrackObject * self)
341 GESTrackObjectPrivate *priv = self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
342 GES_TYPE_TRACK_OBJECT, GESTrackObjectPrivate);
344 /* Sane default values */
345 priv->pending_start = 0;
346 priv->pending_inpoint = 0;
347 priv->pending_duration = GST_SECOND;
348 priv->pending_priority = 1;
349 priv->pending_active = TRUE;
351 priv->properties_hashtable = NULL;
352 priv->maxduration = GST_CLOCK_TIME_NONE;
355 static inline gboolean
356 ges_track_object_set_start_internal (GESTrackObject * object, guint64 start)
358 GST_DEBUG ("object:%p, start:%" GST_TIME_FORMAT,
359 object, GST_TIME_ARGS (start));
361 if (object->priv->gnlobject != NULL) {
362 if (G_UNLIKELY (start == object->start))
365 g_object_set (object->priv->gnlobject, "start", start, NULL);
367 object->priv->pending_start = start;
372 * ges_track_object_set_start:
373 * @object: a #GESTrackObject
374 * @start: the start position (in #GstClockTime)
376 * Sets the position of the object in the container #GESTrack.
379 ges_track_object_set_start (GESTrackObject * object, guint64 start)
381 g_return_if_fail (GES_IS_TRACK_OBJECT (object));
383 if (ges_track_object_set_start_internal (object, start))
384 #if GLIB_CHECK_VERSION(2,26,0)
385 g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_START]);
387 g_object_notify (G_OBJECT (object), "start");
391 static inline gboolean
392 ges_track_object_set_inpoint_internal (GESTrackObject * object, guint64 inpoint)
395 GST_DEBUG ("object:%p, inpoint:%" GST_TIME_FORMAT,
396 object, GST_TIME_ARGS (inpoint));
398 if (object->priv->gnlobject != NULL) {
399 if (G_UNLIKELY (inpoint == object->inpoint))
402 g_object_set (object->priv->gnlobject, "media-start", inpoint, NULL);
404 object->priv->pending_inpoint = inpoint;
410 * ges_track_object_set_inpoint:
411 * @object: a #GESTrackObject
412 * @inpoint: the in-point (in #GstClockTime)
414 * Set the offset within the contents of this #GESTrackObject
417 ges_track_object_set_inpoint (GESTrackObject * object, guint64 inpoint)
419 g_return_if_fail (GES_IS_TRACK_OBJECT (object));
421 if (ges_track_object_set_inpoint_internal (object, inpoint))
422 #if GLIB_CHECK_VERSION(2,26,0)
423 g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_INPOINT]);
425 g_object_notify (G_OBJECT (object), "in-point");
429 static inline gboolean
430 ges_track_object_set_duration_internal (GESTrackObject * object,
433 GESTrackObjectPrivate *priv = object->priv;
435 GST_DEBUG ("object:%p, duration:%" GST_TIME_FORMAT,
436 object, GST_TIME_ARGS (duration));
438 if (GST_CLOCK_TIME_IS_VALID (priv->maxduration) &&
439 duration > object->inpoint + priv->maxduration)
440 duration = priv->maxduration - object->inpoint;
442 if (priv->gnlobject != NULL) {
443 if (G_UNLIKELY (duration == object->duration))
446 g_object_set (priv->gnlobject, "duration", duration,
447 "media-duration", duration, NULL);
449 priv->pending_duration = duration;
455 * ges_track_object_set_duration:
456 * @object: a #GESTrackObject
457 * @duration: the duration (in #GstClockTime)
459 * Set the duration which will be used in the container #GESTrack
460 * starting from the 'in-point'
463 ges_track_object_set_duration (GESTrackObject * object, guint64 duration)
465 g_return_if_fail (GES_IS_TRACK_OBJECT (object));
467 if (ges_track_object_set_duration_internal (object, duration))
468 #if GLIB_CHECK_VERSION(2,26,0)
469 g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_DURATION]);
471 g_object_notify (G_OBJECT (object), "duration");
475 static inline gboolean
476 ges_track_object_set_priority_internal (GESTrackObject * object,
479 GST_DEBUG ("object:%p, priority:%" G_GUINT32_FORMAT, object, priority);
481 if (object->priv->gnlobject != NULL) {
482 if (G_UNLIKELY (priority == object->priority))
485 g_object_set (object->priv->gnlobject, "priority", priority, NULL);
487 object->priv->pending_priority = priority;
492 * ges_track_object_set_priority:
493 * @object: a #GESTrackObject
494 * @priority: the priority
496 * Sets the priority of the object withing the containing #GESTrack.
497 * If two objects intersect over the same region of time, the priority
498 * property is used to decide which one takes precedence.
500 * The highest priority (that supercedes everything) is 0, and then
501 * lowering priorities go in increasing numerical value (with G_MAXUINT32
502 * being the lowest priority).
505 ges_track_object_set_priority (GESTrackObject * object, guint32 priority)
507 if (ges_track_object_set_priority_internal (object, priority))
508 #if GLIB_CHECK_VERSION(2,26,0)
509 g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_PRIORITY]);
511 g_object_notify (G_OBJECT (object), "priority");
517 * ges_track_object_set_active:
518 * @object: a #GESTrackObject
519 * @active: visibility
521 * Sets the usage of the @object. If @active is %TRUE, the object will be used for
522 * playback and rendering, else it will be ignored.
524 * Returns: %TRUE if the property was toggled, else %FALSE
527 ges_track_object_set_active (GESTrackObject * object, gboolean active)
529 g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), FALSE);
531 GST_DEBUG ("object:%p, active:%d", object, active);
533 if (object->priv->gnlobject != NULL) {
534 if (G_UNLIKELY (active == object->active))
537 g_object_set (object->priv->gnlobject, "active", active, NULL);
539 object->priv->pending_active = active;
543 /* Callbacks from the GNonLin object */
545 gnlobject_start_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
546 GESTrackObject * obj)
549 GESTrackObjectClass *klass;
551 klass = GES_TRACK_OBJECT_GET_CLASS (obj);
553 g_object_get (gnlobject, "start", &start, NULL);
555 GST_DEBUG ("gnlobject start : %" GST_TIME_FORMAT " current : %"
556 GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (obj->start));
558 if (start != obj->start) {
560 if (klass->start_changed)
561 klass->start_changed (obj, start);
566 gst_element_prop_changed_cb (GstElement * element, GParamSpec * arg
567 G_GNUC_UNUSED, GESTrackObject * obj)
569 g_signal_emit (obj, ges_track_object_signals[DEEP_NOTIFY], 0,
570 GST_ELEMENT (element), arg);
574 connect_signal (gpointer key, gpointer value, gpointer user_data)
576 gchar *signame = g_strconcat ("notify::", G_PARAM_SPEC (key)->name, NULL);
578 g_signal_connect (G_OBJECT (value),
579 signame, G_CALLBACK (gst_element_prop_changed_cb),
580 GES_TRACK_OBJECT (user_data));
586 connect_properties_signals (GESTrackObject * object)
588 if (G_UNLIKELY (!object->priv->properties_hashtable)) {
589 GST_WARNING ("The properties_hashtable hasn't been set");
593 g_hash_table_foreach (object->priv->properties_hashtable,
594 (GHFunc) connect_signal, object);
598 /* Callbacks from the GNonLin object */
600 gnlobject_media_start_cb (GstElement * gnlobject,
601 GParamSpec * arg G_GNUC_UNUSED, GESTrackObject * obj)
604 GESTrackObjectClass *klass;
606 klass = GES_TRACK_OBJECT_GET_CLASS (obj);
608 g_object_get (gnlobject, "media-start", &start, NULL);
610 GST_DEBUG ("gnlobject in-point : %" GST_TIME_FORMAT " current : %"
611 GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (obj->inpoint));
613 if (start != obj->inpoint) {
614 obj->inpoint = start;
615 if (klass->media_start_changed)
616 klass->media_start_changed (obj, start);
621 gnlobject_priority_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
622 GESTrackObject * obj)
625 GESTrackObjectClass *klass;
627 klass = GES_TRACK_OBJECT_GET_CLASS (obj);
629 g_object_get (gnlobject, "priority", &priority, NULL);
631 GST_DEBUG ("gnlobject priority : %d current : %d", priority, obj->priority);
633 if (priority != obj->priority) {
634 obj->priority = priority;
635 if (klass->gnl_priority_changed)
636 klass->gnl_priority_changed (obj, priority);
641 gnlobject_duration_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
642 GESTrackObject * obj)
645 GESTrackObjectClass *klass;
647 klass = GES_TRACK_OBJECT_GET_CLASS (obj);
649 g_object_get (gnlobject, "duration", &duration, NULL);
651 GST_DEBUG_OBJECT (gnlobject, "duration : %" GST_TIME_FORMAT " current : %"
652 GST_TIME_FORMAT, GST_TIME_ARGS (duration), GST_TIME_ARGS (obj->duration));
654 if (duration != obj->duration) {
655 obj->duration = duration;
656 if (klass->duration_changed)
657 klass->duration_changed (obj, duration);
662 gnlobject_active_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
663 GESTrackObject * obj)
666 GESTrackObjectClass *klass;
668 klass = GES_TRACK_OBJECT_GET_CLASS (obj);
670 g_object_get (gnlobject, "active", &active, NULL);
672 GST_DEBUG ("gnlobject active : %d current : %d", active, obj->active);
674 if (active != obj->active) {
675 obj->active = active;
676 if (klass->active_changed)
677 klass->active_changed (obj, active);
682 /* default 'create_gnl_object' virtual method implementation */
684 ges_track_object_create_gnl_object_func (GESTrackObject * self)
686 GESTrackObjectClass *klass = NULL;
687 GstElement *child = NULL;
688 GstElement *gnlobject;
690 klass = GES_TRACK_OBJECT_GET_CLASS (self);
692 if (G_UNLIKELY (self->priv->gnlobject != NULL))
693 goto already_have_gnlobject;
695 if (G_UNLIKELY (klass->gnlobject_factorytype == NULL))
698 GST_DEBUG ("Creating a supporting gnlobject of type '%s'",
699 klass->gnlobject_factorytype);
701 gnlobject = gst_element_factory_make (klass->gnlobject_factorytype, NULL);
703 if (G_UNLIKELY (gnlobject == NULL))
706 if (klass->create_element) {
707 GST_DEBUG ("Calling subclass 'create_element' vmethod");
708 child = klass->create_element (self);
710 if (G_UNLIKELY (!child))
713 if (!gst_bin_add (GST_BIN (gnlobject), child))
716 GST_DEBUG ("Succesfully got the element to put in the gnlobject");
717 self->priv->element = child;
726 already_have_gnlobject:
728 GST_ERROR ("Already controlling a GnlObject %s",
729 GST_ELEMENT_NAME (self->priv->gnlobject));
735 GST_ERROR ("No GESTrackObject::gnlobject_factorytype implementation!");
741 GST_ERROR ("Error creating a gnlobject of type '%s'",
742 klass->gnlobject_factorytype);
748 GST_ERROR ("create_element returned NULL");
749 gst_object_unref (gnlobject);
755 GST_ERROR ("Error adding the contents to the gnlobject");
756 gst_object_unref (child);
757 gst_object_unref (gnlobject);
763 ensure_gnl_object (GESTrackObject * object)
765 GESTrackObjectClass *class;
766 GstElement *gnlobject;
767 GHashTable *props_hash;
770 if (object->priv->gnlobject && object->priv->valid)
773 /* 1. Create the GnlObject */
774 GST_DEBUG ("Creating GnlObject");
776 class = GES_TRACK_OBJECT_GET_CLASS (object);
778 if (G_UNLIKELY (class->create_gnl_object == NULL)) {
779 GST_ERROR ("No 'create_gnl_object' implementation !");
783 GST_DEBUG ("Calling virtual method");
785 /* 2. Fill in the GnlObject */
786 if (object->priv->gnlobject == NULL) {
788 /* call the create_gnl_object virtual method */
789 gnlobject = class->create_gnl_object (object);
791 if (G_UNLIKELY (gnlobject == NULL)) {
793 ("'create_gnl_object' implementation returned TRUE but no GnlObject is available");
797 GST_DEBUG_OBJECT (object, "Got a valid GnlObject, now filling it in");
799 object->priv->gnlobject = gnlobject;
801 if (object->priv->timelineobj)
802 res = ges_timeline_object_fill_track_object (object->priv->timelineobj,
803 object, object->priv->gnlobject);
808 /* Connect to property notifications */
809 /* FIXME : remember the signalids so we can remove them later on !!! */
810 g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::start",
811 G_CALLBACK (gnlobject_start_cb), object);
812 g_signal_connect (G_OBJECT (object->priv->gnlobject),
813 "notify::media-start", G_CALLBACK (gnlobject_media_start_cb), object);
814 g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::duration",
815 G_CALLBACK (gnlobject_duration_cb), object);
816 g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::priority",
817 G_CALLBACK (gnlobject_priority_cb), object);
818 g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::active",
819 G_CALLBACK (gnlobject_active_cb), object);
821 /* Set some properties on the GnlObject */
822 g_object_set (object->priv->gnlobject,
823 "duration", object->priv->pending_duration,
824 "media-duration", object->priv->pending_duration,
825 "start", object->priv->pending_start,
826 "media-start", object->priv->pending_inpoint,
827 "priority", object->priv->pending_priority,
828 "active", object->priv->pending_active, NULL);
830 if (object->priv->track != NULL)
831 g_object_set (object->priv->gnlobject,
832 "caps", ges_track_get_caps (object->priv->track), NULL);
834 /* We feed up the props_hashtable if possible */
835 if (class->get_props_hastable) {
836 props_hash = class->get_props_hastable (object);
838 if (props_hash == NULL) {
839 GST_DEBUG ("'get_props_hastable' implementation returned TRUE but no"
840 "properties_hashtable is available");
842 object->priv->properties_hashtable = props_hash;
843 connect_properties_signals (object);
850 object->priv->valid = res;
852 GST_DEBUG ("Returning res:%d", res);
859 ges_track_object_set_track (GESTrackObject * object, GESTrack * track)
861 GST_DEBUG ("object:%p, track:%p", object, track);
863 object->priv->track = track;
865 if (object->priv->track) {
866 /* If we already have a gnlobject, we just set its caps properly */
867 if (object->priv->gnlobject) {
868 g_object_set (object->priv->gnlobject,
869 "caps", ges_track_get_caps (object->priv->track), NULL);
872 return ensure_gnl_object (object);
880 * ges_track_object_get_track:
881 * @object: a #GESTrackObject
883 * Get the #GESTrack to which this object belongs.
885 * Returns: (transfer none): The #GESTrack to which this object belongs. Can be %NULL if it
886 * is not in any track
889 ges_track_object_get_track (GESTrackObject * object)
891 g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), NULL);
893 return object->priv->track;
897 * ges_track_object_set_timeline_object:
898 * @object: The #GESTrackObject to set the parent to
899 * @tlobject: The #GESTimelineObject, parent of @tlobj or %NULL
901 * Set the #GESTimelineObject to which @object belongs.
904 ges_track_object_set_timeline_object (GESTrackObject * object,
905 GESTimelineObject * tlobject)
907 GST_DEBUG ("object:%p, timeline-object:%p", object, tlobject);
909 object->priv->timelineobj = tlobject;
913 * ges_track_object_get_timeline_object:
914 * @object: a #GESTrackObject
916 * Get the #GESTimelineObject which is controlling this track object
918 * Returns: (transfer none): the #GESTimelineObject which is controlling
922 ges_track_object_get_timeline_object (GESTrackObject * object)
924 g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), NULL);
926 return object->priv->timelineobj;
930 * ges_track_object_get_gnlobject:
931 * @object: a #GESTrackObject
933 * Get the GNonLin object this object is controlling.
935 * Returns: (transfer none): the GNonLin object this object is controlling.
938 ges_track_object_get_gnlobject (GESTrackObject * object)
940 return object->priv->gnlobject;
944 * ges_track_object_get_element:
945 * @object: a #GESTrackObject
947 * Get the #GstElement this track object is controlling within GNonLin.
949 * Returns: (transfer none): the #GstElement this track object is controlling
953 ges_track_object_get_element (GESTrackObject * object)
955 return object->priv->element;
959 ges_track_object_set_locked_internal (GESTrackObject * object, gboolean locked)
961 object->priv->locked = locked;
965 * ges_track_object_set_locked:
966 * @object: a #GESTrackObject
967 * @locked: whether the object is lock to its parent
969 * Set the locking status of the @object in relationship to its controlling
970 * #GESTimelineObject. If @locked is %TRUE, then this object will move synchronously
971 * with its controlling #GESTimelineObject.
974 ges_track_object_set_locked (GESTrackObject * object, gboolean locked)
976 g_return_if_fail (GES_IS_TRACK_OBJECT (object));
978 GST_DEBUG_OBJECT (object, "%s object", locked ? "Locking" : "Unlocking");
980 ges_track_object_set_locked_internal (object, locked);
981 #if GLIB_CHECK_VERSION(2,26,0)
982 g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_LOCKED]);
984 g_object_notify (G_OBJECT (object), "locked");
990 * ges_track_object_is_locked:
991 * @object: a #GESTrackObject
993 * Let you know if object us locked or not (moving synchronously).
995 * Returns: %TRUE if the object is moving synchronously to its controlling
996 * #GESTimelineObject, else %FALSE.
999 ges_track_object_is_locked (GESTrackObject * object)
1001 g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), FALSE);
1003 return object->priv->locked;
1007 * ges_track_object_get_start:
1008 * @object: a #GESTrackObject
1010 * Get the position of the object in the container #GESTrack.
1012 * Returns: the start position (in #GstClockTime) or #GST_CLOCK_TIME_NONE
1013 * if something went wrong.
1018 ges_track_object_get_start (GESTrackObject * object)
1020 g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), GST_CLOCK_TIME_NONE);
1022 if (G_UNLIKELY (object->priv->gnlobject == NULL))
1023 return object->priv->pending_start;
1025 return object->start;
1029 * ges_track_object_get_inpoint:
1030 * @object: a #GESTrackObject
1032 * Get the offset within the contents of this #GESTrackObject
1034 * Returns: the in-point (in #GstClockTime) or #GST_CLOCK_TIME_NONE
1035 * if something went wrong.
1040 ges_track_object_get_inpoint (GESTrackObject * object)
1042 g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), GST_CLOCK_TIME_NONE);
1044 if (G_UNLIKELY (object->priv->gnlobject == NULL))
1045 return object->priv->pending_inpoint;
1047 return object->inpoint;
1051 * ges_track_object_get_duration:
1052 * @object: a #GESTrackObject
1054 * Get the duration which will be used in the container #GESTrack
1055 * starting from the 'in-point'
1057 * Returns: the duration (in #GstClockTime) or #GST_CLOCK_TIME_NONE
1058 * if something went wrong.
1063 ges_track_object_get_duration (GESTrackObject * object)
1065 g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), GST_CLOCK_TIME_NONE);
1067 if (G_UNLIKELY (object->priv->gnlobject == NULL))
1068 return object->priv->pending_duration;
1070 return object->duration;
1074 * ges_track_object_get_priority:
1075 * @object: a #GESTrackObject
1077 * Get the priority of the object withing the containing #GESTrack.
1079 * Returns: the priority of @object or -1 if something went wrong
1084 ges_track_object_get_priority (GESTrackObject * object)
1086 g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), -1);
1088 if (G_UNLIKELY (object->priv->gnlobject == NULL))
1089 return object->priv->pending_priority;
1091 return object->priority;
1095 * ges_track_object_is_active:
1096 * @object: a #GESTrackObject
1098 * Lets you know if @object will be used for playback and rendering,
1101 * Returns: %TRUE if @object is active, %FALSE otherwize
1106 ges_track_object_is_active (GESTrackObject * object)
1108 g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), FALSE);
1110 if (G_UNLIKELY (object->priv->gnlobject == NULL))
1111 return object->priv->pending_active;
1113 return object->active;
1117 * ges_track_object_lookup_child:
1118 * @object: object to lookup the property in
1119 * @prop_name: name of the property to look up. You can specify the name of the
1120 * class as such: "ClassName::property-name", to guarantee that you get the
1121 * proper GParamSpec in case various GstElement-s contain the same property
1122 * name. If you don't do so, you will get the first element found, having
1123 * this property and the and the corresponding GParamSpec.
1124 * @element: (out) (allow-none) (transfer full): pointer to a #GstElement that
1125 * takes the real object to set property on
1126 * @pspec: (out) (allow-none) (transfer full): pointer to take the #GParamSpec
1127 * describing the property
1129 * Looks up which @element and @pspec would be effected by the given @name. If various
1130 * contained elements have this property name you will get the first one, unless you
1131 * specify the class name in @name.
1133 * Returns: TRUE if @element and @pspec could be found. FALSE otherwise. In that
1134 * case the values for @pspec and @element are not modified. Unref @element after
1140 ges_track_object_lookup_child (GESTrackObject * object, const gchar * prop_name,
1141 GstElement ** element, GParamSpec ** pspec)
1143 GHashTableIter iter;
1144 gpointer key, value;
1145 gchar **names, *name, *classename;
1147 GESTrackObjectPrivate *priv;
1149 g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), FALSE);
1151 priv = object->priv;
1156 names = g_strsplit (prop_name, "::", 2);
1157 if (names[1] != NULL) {
1158 classename = names[0];
1163 g_hash_table_iter_init (&iter, priv->properties_hashtable);
1164 while (g_hash_table_iter_next (&iter, &key, &value)) {
1165 if (g_strcmp0 (G_PARAM_SPEC (key)->name, name) == 0) {
1166 if (classename == NULL ||
1167 g_strcmp0 (G_OBJECT_TYPE_NAME (G_OBJECT (value)), classename) == 0) {
1168 GST_DEBUG ("The %s property from %s has been found", name, classename);
1170 *element = g_object_ref (value);
1172 *pspec = g_param_spec_ref (key);
1184 * ges_track_object_set_child_property_by_pspec:
1185 * @object: a #GESTrackObject
1186 * @pspec: The #GParamSpec that specifies the property you want to set
1189 * Sets a property of a child of @object.
1194 ges_track_object_set_child_property_by_pspec (GESTrackObject * object,
1195 GParamSpec * pspec, GValue * value)
1197 GstElement *element;
1198 GESTrackObjectPrivate *priv;
1200 g_return_if_fail (GES_IS_TRACK_OBJECT (object));
1202 priv = object->priv;
1204 if (!priv->properties_hashtable)
1205 goto prop_hash_not_set;
1207 element = g_hash_table_lookup (priv->properties_hashtable, pspec);
1211 g_object_set_property (G_OBJECT (element), pspec->name, value);
1217 GST_ERROR ("The %s property doesn't exist", pspec->name);
1222 GST_DEBUG ("The child properties haven't been set on %p", object);
1228 * ges_track_object_set_child_property_valist:
1229 * @object: The #GESTrackObject parent object
1230 * @first_property_name: The name of the first property to set
1231 * @var_args: value for the first property, followed optionally by more
1232 * name/return location pairs, followed by NULL
1234 * Sets a property of a child of @object. If there are various child elements
1235 * that have the same property name, you can distinguish them using the following
1236 * syntax: 'ClasseName::property_name' as property name. If you don't, the
1237 * corresponding property of the first element found will be set.
1242 ges_track_object_set_child_property_valist (GESTrackObject * object,
1243 const gchar * first_property_name, va_list var_args)
1247 GstElement *element;
1249 gchar *error = NULL;
1250 GValue value = { 0, };
1252 g_return_if_fail (GES_IS_TRACK_OBJECT (object));
1254 name = first_property_name;
1256 /* Note: This part is in big part copied from the gst_child_object_set_valist
1259 /* iterate over pairs */
1261 if (!ges_track_object_lookup_child (object, name, &element, &pspec))
1264 #if GLIB_CHECK_VERSION(2,23,3)
1265 G_VALUE_COLLECT_INIT (&value, pspec->value_type, var_args,
1266 G_VALUE_NOCOPY_CONTENTS, &error);
1268 g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1269 G_VALUE_COLLECT (&value, var_args, G_VALUE_NOCOPY_CONTENTS, &error);
1275 g_object_set_property (G_OBJECT (element), pspec->name, &value);
1277 g_object_unref (element);
1278 g_value_unset (&value);
1280 name = va_arg (var_args, gchar *);
1286 GST_WARNING ("No property %s in OBJECT\n", name);
1291 GST_WARNING ("error copying value %s in object %p: %s", pspec->name, object,
1293 g_value_unset (&value);
1299 * ges_track_object_set_child_property:
1300 * @object: The #GESTrackObject parent object
1301 * @first_property_name: The name of the first property to set
1302 * @...: value for the first property, followed optionally by more
1303 * name/return location pairs, followed by NULL
1305 * Sets a property of a child of @object. If there are various child elements
1306 * that have the same property name, you can distinguish them using the following
1307 * syntax: 'ClasseName::property_name' as property name. If you don't, the
1308 * corresponding property of the first element found will be set.
1313 ges_track_object_set_child_property (GESTrackObject * object,
1314 const gchar * first_property_name, ...)
1318 g_return_if_fail (GES_IS_TRACK_OBJECT (object));
1320 va_start (var_args, first_property_name);
1321 ges_track_object_set_child_property_valist (object, first_property_name,
1327 * ges_track_object_get_child_property_valist:
1328 * @object: The #GESTrackObject parent object
1329 * @first_property_name: The name of the first property to get
1330 * @var_args: value for the first property, followed optionally by more
1331 * name/return location pairs, followed by NULL
1333 * Gets a property of a child of @object. If there are various child elements
1334 * that have the same property name, you can distinguish them using the following
1335 * syntax: 'ClasseName::property_name' as property name. If you don't, the
1336 * corresponding property of the first element found will be set.
1341 ges_track_object_get_child_property_valist (GESTrackObject * object,
1342 const gchar * first_property_name, va_list var_args)
1345 gchar *error = NULL;
1346 GValue value = { 0, };
1348 GstElement *element;
1350 g_return_if_fail (G_IS_OBJECT (object));
1352 name = first_property_name;
1354 /* This part is in big part copied from the gst_child_object_get_valist method */
1356 if (!ges_track_object_lookup_child (object, name, &element, &pspec))
1359 g_value_init (&value, pspec->value_type);
1360 g_object_get_property (G_OBJECT (element), pspec->name, &value);
1361 g_object_unref (element);
1363 G_VALUE_LCOPY (&value, var_args, 0, &error);
1366 g_value_unset (&value);
1367 name = va_arg (var_args, gchar *);
1373 GST_WARNING ("no property %s in object", name);
1378 GST_WARNING ("error copying value %s in object %p: %s", pspec->name, object,
1380 g_value_unset (&value);
1386 * ges_track_object_list_children_properties:
1387 * @object: The #GESTrackObject to get the list of children properties from
1388 * @n_properties: return location for the length of the returned array
1390 * Gets an array of #GParamSpec* for all configurable properties of the
1391 * children of @object.
1393 * Returns: (transfer full) (array): an array of #GParamSpec* which should be freed after use or
1394 * %NULL if something went wrong
1399 ges_track_object_list_children_properties (GESTrackObject * object,
1400 guint * n_properties)
1402 GESTrackObjectClass *class;
1404 g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), NULL);
1406 class = GES_TRACK_OBJECT_GET_CLASS (object);
1408 return class->list_children_properties (object, n_properties);
1412 * ges_track_object_get_child_property:
1413 * @object: The origin #GESTrackObject
1414 * @first_property_name: The name of the first property to get
1415 * @...: return location for the first property, followed optionally by more
1416 * name/return location pairs, followed by NULL
1418 * Gets properties of a child of @object.
1423 ges_track_object_get_child_property (GESTrackObject * object,
1424 const gchar * first_property_name, ...)
1428 g_return_if_fail (GES_IS_TRACK_OBJECT (object));
1430 va_start (var_args, first_property_name);
1431 ges_track_object_get_child_property_valist (object, first_property_name,
1437 * ges_track_object_get_child_property_by_pspec:
1438 * @object: a #GESTrackObject
1439 * @pspec: The #GParamSpec that specifies the property you want to get
1440 * @value: return location for the value
1442 * Gets a property of a child of @object.
1447 ges_track_object_get_child_property_by_pspec (GESTrackObject * object,
1448 GParamSpec * pspec, GValue * value)
1450 GstElement *element;
1451 GESTrackObjectPrivate *priv;
1453 g_return_if_fail (GES_IS_TRACK_OBJECT (object));
1455 priv = object->priv;
1457 if (!priv->properties_hashtable)
1458 goto prop_hash_not_set;
1460 element = g_hash_table_lookup (priv->properties_hashtable, pspec);
1464 g_object_get_property (G_OBJECT (element), pspec->name, value);
1470 GST_ERROR ("The %s property doesn't exist", pspec->name);
1475 GST_ERROR ("The child properties haven't been set on %p", object);
1480 static GParamSpec **
1481 default_list_children_properties (GESTrackObject * object, guint * n_properties)
1483 GParamSpec **pspec, *spec;
1484 GHashTableIter iter;
1485 gpointer key, value;
1489 if (!object->priv->properties_hashtable)
1490 goto prop_hash_not_set;
1492 *n_properties = g_hash_table_size (object->priv->properties_hashtable);
1493 pspec = g_new (GParamSpec *, *n_properties);
1495 g_hash_table_iter_init (&iter, object->priv->properties_hashtable);
1496 while (g_hash_table_iter_next (&iter, &key, &value)) {
1497 spec = G_PARAM_SPEC (key);
1498 pspec[i] = g_param_spec_ref (spec);
1507 GST_ERROR ("The child properties haven't been set on %p", object);
1513 * ges_track_object_get_max_duration:
1514 * @object: The #GESTrackObject to retrieve max duration from
1516 * Get the max duration of @object.
1518 * Returns: The max duration of @object
1523 ges_track_object_get_max_duration (GESTrackObject * object)
1525 g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), 0);
1527 return object->priv->maxduration;
1531 * ges_track_object_set_max_duration:
1532 * @object: The #GESTrackObject to retrieve max duration from
1533 * @maxduration: The maximum duration of @object
1535 * Returns: Set the max duration of @object
1540 ges_track_object_set_max_duration (GESTrackObject * object, guint64 maxduration)
1542 g_return_if_fail (GES_IS_TRACK_OBJECT (object));
1544 object->priv->maxduration = maxduration;
1548 * ges_track_object_copy:
1549 * @object: The #GESTrackObject to copy
1550 * @deep: whether we want to create the gnlobject and copy it properties
1554 * Returns: The newly create #GESTrackObject, copied from @object
1559 ges_track_object_copy (GESTrackObject * object, gboolean deep)
1561 GESTrackObject *ret = NULL;
1564 guint n, n_specs, n_params;
1567 g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), NULL);
1570 g_object_class_list_properties (G_OBJECT_GET_CLASS (object), &n_specs);
1571 params = g_new0 (GParameter, n_specs);
1574 for (n = 0; n < n_specs; ++n) {
1575 if (g_strcmp0 (specs[n]->name, "parent") &&
1576 (specs[n]->flags & G_PARAM_READWRITE) == G_PARAM_READWRITE) {
1577 params[n_params].name = g_intern_string (specs[n]->name);
1578 g_value_init (¶ms[n_params].value, specs[n]->value_type);
1579 g_object_get_property (G_OBJECT (object), specs[n]->name,
1580 ¶ms[n_params].value);
1585 ret = g_object_newv (G_TYPE_FROM_INSTANCE (object), n_params, params);
1594 ensure_gnl_object (ret);
1595 specs = ges_track_object_list_children_properties (object, &n_specs);
1596 for (n = 0; n < n_specs; ++n) {
1597 g_value_init (&val, specs[n]->value_type);
1598 g_object_get_property (G_OBJECT (object), specs[n]->name, &val);
1599 ges_track_object_set_child_property_by_pspec (ret, specs[n], &val);
1600 g_value_unset (&val);
1610 * ges_track_object_edit:
1611 * @object: the #GESTrackObject to edit
1612 * @layers: (element-type GESTimelineLayer): The layers you want the edit to
1613 * happen in, %NULL means that the edition is done in all the
1614 * #GESTimelineLayers contained in the current timeline.
1615 * FIXME: This is not implemented yet.
1616 * @mode: The #GESEditMode in which the editition will happen.
1617 * @edge: The #GESEdge the edit should happen on.
1618 * @position: The position at which to edit @object (in nanosecond)
1620 * Edit @object in the different exisiting #GESEditMode modes. In the case of
1621 * slide, and roll, you need to specify a #GESEdge
1623 * Returns: %TRUE if the object as been edited properly, %FALSE if an error
1629 ges_track_object_edit (GESTrackObject * object,
1630 GList * layers, GESEditMode mode, GESEdge edge, guint64 position)
1632 GESTrack *track = ges_track_object_get_track (object);
1633 GESTimeline *timeline;
1635 g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), FALSE);
1637 if (G_UNLIKELY (!track)) {
1638 GST_WARNING_OBJECT (object, "Trying to edit in %d mode but not in"
1639 "any Track yet.", mode);
1641 } else if (position < 0) {
1642 GST_DEBUG_OBJECT (object, "Trying to move before 0, not moving");
1646 timeline = GES_TIMELINE (ges_track_get_timeline (track));
1648 if (G_UNLIKELY (!timeline)) {
1649 GST_WARNING_OBJECT (object, "Trying to edit in %d mode but not in"
1650 "track %p no in any timeline yet.", mode, track);
1655 case GES_EDIT_MODE_NORMAL:
1656 timeline_move_object (timeline, object, layers, edge, position);
1658 case GES_EDIT_MODE_TRIM:
1659 timeline_trim_object (timeline, object, layers, edge, position);
1661 case GES_EDIT_MODE_RIPPLE:
1662 timeline_ripple_object (timeline, object, layers, edge, position);
1664 case GES_EDIT_MODE_ROLL:
1665 timeline_roll_object (timeline, object, layers, edge, position);
1667 case GES_EDIT_MODE_SLIDE:
1668 timeline_slide_object (timeline, object, layers, edge, position);
1671 GST_ERROR ("Unkown edit mode: %d", mode);