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 "ges-track-object.h"
35 #include "ges-timeline-object.h"
37 G_DEFINE_ABSTRACT_TYPE (GESTrackObject, ges_track_object,
38 G_TYPE_INITIALLY_UNOWNED);
40 struct _GESTrackObjectPrivate
42 /* These fields are only used before the gnlobject is available */
43 guint64 pending_start;
44 guint64 pending_inpoint;
45 guint64 pending_duration;
46 guint32 pending_priority;
47 gboolean pending_active;
49 GstElement *gnlobject; /* The GnlObject */
50 GstElement *element; /* The element contained in the gnlobject (can be NULL) */
52 GESTimelineObject *timelineobj;
57 gboolean locked; /* If TRUE, then moves in sync with its controlling
58 * GESTimelineObject */
72 static GParamSpec *properties[PROP_LAST];
74 static GstElement *ges_track_object_create_gnl_object_func (GESTrackObject *
77 void gnlobject_start_cb (GstElement * gnlobject, GParamSpec * arg
78 G_GNUC_UNUSED, GESTrackObject * obj);
80 void gnlobject_media_start_cb (GstElement * gnlobject, GParamSpec * arg
81 G_GNUC_UNUSED, GESTrackObject * obj);
83 void gnlobject_priority_cb (GstElement * gnlobject, GParamSpec * arg
84 G_GNUC_UNUSED, GESTrackObject * obj);
86 void gnlobject_duration_cb (GstElement * gnlobject, GParamSpec * arg
87 G_GNUC_UNUSED, GESTrackObject * obj);
89 void gnlobject_active_cb (GstElement * gnlobject, GParamSpec * arg
90 G_GNUC_UNUSED, GESTrackObject * obj);
92 static inline gboolean
93 ges_track_object_set_start_internal (GESTrackObject * object, guint64 start);
94 static inline gboolean
95 ges_track_object_set_inpoint_internal (GESTrackObject * object,
97 static inline gboolean ges_track_object_set_duration_internal (GESTrackObject *
98 object, guint64 duration);
99 static inline gboolean ges_track_object_set_priority_internal (GESTrackObject *
100 object, guint32 priority);
103 ges_track_object_get_property (GObject * object, guint property_id,
104 GValue * value, GParamSpec * pspec)
106 GESTrackObject *tobj = GES_TRACK_OBJECT (object);
108 switch (property_id) {
110 g_value_set_uint64 (value, tobj->start);
113 g_value_set_uint64 (value, tobj->inpoint);
116 g_value_set_uint64 (value, tobj->duration);
119 g_value_set_uint (value, tobj->priority);
122 g_value_set_boolean (value, tobj->active);
125 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
130 ges_track_object_set_property (GObject * object, guint property_id,
131 const GValue * value, GParamSpec * pspec)
133 GESTrackObject *tobj = GES_TRACK_OBJECT (object);
135 switch (property_id) {
137 ges_track_object_set_start_internal (tobj, g_value_get_uint64 (value));
140 ges_track_object_set_inpoint_internal (tobj, g_value_get_uint64 (value));
143 ges_track_object_set_duration_internal (tobj, g_value_get_uint64 (value));
146 ges_track_object_set_priority_internal (tobj, g_value_get_uint (value));
149 ges_track_object_set_active (tobj, g_value_get_boolean (value));
152 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
157 ges_track_object_dispose (GObject * object)
159 G_OBJECT_CLASS (ges_track_object_parent_class)->dispose (object);
163 ges_track_object_finalize (GObject * object)
165 G_OBJECT_CLASS (ges_track_object_parent_class)->finalize (object);
169 ges_track_object_class_init (GESTrackObjectClass * klass)
171 GObjectClass *object_class = G_OBJECT_CLASS (klass);
173 g_type_class_add_private (klass, sizeof (GESTrackObjectPrivate));
175 object_class->get_property = ges_track_object_get_property;
176 object_class->set_property = ges_track_object_set_property;
177 object_class->dispose = ges_track_object_dispose;
178 object_class->finalize = ges_track_object_finalize;
181 * GESTrackObject:start
183 * The position of the object in the container #GESTrack (in nanoseconds).
185 properties[PROP_START] = g_param_spec_uint64 ("start", "Start",
186 "The position in the container", 0, G_MAXUINT64, 0, G_PARAM_READWRITE);
187 g_object_class_install_property (object_class, PROP_START,
188 properties[PROP_START]);
191 * GESTrackObject:in-point
193 * The in-point at which this #GESTrackObject will start outputting data
194 * from its contents (in nanoseconds).
196 * Ex : an in-point of 5 seconds means that the first outputted buffer will
197 * be the one located 5 seconds in the controlled resource.
199 properties[PROP_INPOINT] =
200 g_param_spec_uint64 ("in-point", "In-point", "The in-point", 0,
201 G_MAXUINT64, 0, G_PARAM_READWRITE);
202 g_object_class_install_property (object_class, PROP_INPOINT,
203 properties[PROP_INPOINT]);
206 * GESTrackObject:duration
208 * The duration (in nanoseconds) which will be used in the container #GESTrack
209 * starting from 'in-point'.
212 properties[PROP_DURATION] =
213 g_param_spec_uint64 ("duration", "Duration", "The duration to use", 0,
214 G_MAXUINT64, GST_SECOND, G_PARAM_READWRITE);
215 g_object_class_install_property (object_class, PROP_DURATION,
216 properties[PROP_DURATION]);
219 * GESTrackObject:priority
221 * The priority of the object within the containing #GESTrack.
222 * If two objects intersect over the same region of time, the @priority
223 * property is used to decide which one takes precedence.
225 * The highest priority (that supercedes everything) is 0, and then lowering
226 * priorities go in increasing numerical value (with #G_MAXUINT64 being the
229 properties[PROP_PRIORITY] = g_param_spec_uint ("priority", "Priority",
230 "The priority of the object", 0, G_MAXUINT, 0, G_PARAM_READWRITE);
231 g_object_class_install_property (object_class, PROP_PRIORITY,
232 properties[PROP_PRIORITY]);
235 * GESTrackObject:active
237 * Whether the object should be taken into account in the #GESTrack output.
238 * If #FALSE, then its contents will not be used in the resulting track.
240 properties[PROP_ACTIVE] =
241 g_param_spec_boolean ("active", "Active", "Use object in output", TRUE,
243 g_object_class_install_property (object_class, PROP_ACTIVE,
244 properties[PROP_ACTIVE]);
246 klass->create_gnl_object = ges_track_object_create_gnl_object_func;
250 ges_track_object_init (GESTrackObject * self)
252 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
253 GES_TYPE_TRACK_OBJECT, GESTrackObjectPrivate);
255 /* Sane default values */
256 self->priv->pending_start = 0;
257 self->priv->pending_inpoint = 0;
258 self->priv->pending_duration = GST_SECOND;
259 self->priv->pending_priority = 1;
260 self->priv->pending_active = TRUE;
261 self->priv->locked = TRUE;
264 static inline gboolean
265 ges_track_object_set_start_internal (GESTrackObject * object, guint64 start)
267 GST_DEBUG ("object:%p, start:%" GST_TIME_FORMAT,
268 object, GST_TIME_ARGS (start));
270 if (object->priv->gnlobject != NULL) {
271 if (G_UNLIKELY (start == object->start))
274 g_object_set (object->priv->gnlobject, "start", start, NULL);
276 object->priv->pending_start = start;
281 * ges_track_object_set_start:
282 * @object: a #GESTrackObject
283 * @start: the start position (in #GstClockTime)
285 * Sets the position of the object in the container #GESTrack.
288 ges_track_object_set_start (GESTrackObject * object, guint64 start)
290 if (ges_track_object_set_start_internal (object, start))
291 #if GLIB_CHECK_VERSION(2,26,0)
292 g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_START]);
294 g_object_notify (G_OBJECT (object), "start");
298 static inline gboolean
299 ges_track_object_set_inpoint_internal (GESTrackObject * object, guint64 inpoint)
302 GST_DEBUG ("object:%p, inpoint:%" GST_TIME_FORMAT,
303 object, GST_TIME_ARGS (inpoint));
305 if (object->priv->gnlobject != NULL) {
306 if (G_UNLIKELY (inpoint == object->inpoint))
309 g_object_set (object->priv->gnlobject, "media-start", inpoint, NULL);
311 object->priv->pending_inpoint = inpoint;
317 * ges_track_object_set_inpoint:
318 * @object: a #GESTrackObject
319 * @inpoint: the in-point (in #GstClockTime)
321 * Set the offset within the contents of this #GESTrackObject
324 ges_track_object_set_inpoint (GESTrackObject * object, guint64 inpoint)
326 if (ges_track_object_set_inpoint_internal (object, inpoint))
327 #if GLIB_CHECK_VERSION(2,26,0)
328 g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_INPOINT]);
330 g_object_notify (G_OBJECT (object), "in-point");
334 static inline gboolean
335 ges_track_object_set_duration_internal (GESTrackObject * object,
338 GST_DEBUG ("object:%p, duration:%" GST_TIME_FORMAT,
339 object, GST_TIME_ARGS (duration));
341 if (object->priv->gnlobject != NULL) {
342 if (G_UNLIKELY (duration == object->duration))
345 g_object_set (object->priv->gnlobject, "duration", duration,
346 "media-duration", duration, NULL);
348 object->priv->pending_duration = duration;
353 * ges_track_object_set_duration:
354 * @object: a #GESTrackObject
355 * @duration: the duration (in #GstClockTime)
357 * Set the duration which will be used in the container #GESTrack
358 * starting from the 'in-point'
361 ges_track_object_set_duration (GESTrackObject * object, guint64 duration)
363 if (ges_track_object_set_duration_internal (object, duration))
364 #if GLIB_CHECK_VERSION(2,26,0)
365 g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_DURATION]);
367 g_object_notify (G_OBJECT (object), "duration");
371 static inline gboolean
372 ges_track_object_set_priority_internal (GESTrackObject * object,
375 GST_DEBUG ("object:%p, priority:%" GST_TIME_FORMAT,
376 object, GST_TIME_ARGS (priority));
378 if (object->priv->gnlobject != NULL) {
379 if (G_UNLIKELY (priority == object->priority))
382 g_object_set (object->priv->gnlobject, "priority", priority, NULL);
384 object->priv->pending_priority = priority;
389 * ges_track_object_set_priority:
390 * @object: a #GESTrackObject
391 * @priority: the priority
393 * Sets the priority of the object withing the containing #GESTrack.
394 * If two objects intersect over the same region of time, the priority
395 * property is used to decide which one takes precedence.
397 * The highest priority (that supercedes everything) is 0, and then
398 * lowering priorities go in increasing numerical value (with G_MAXUINT32
399 * being the lowest priority).
402 ges_track_object_set_priority (GESTrackObject * object, guint32 priority)
404 if (ges_track_object_set_priority_internal (object, priority))
405 #if GLIB_CHECK_VERSION(2,26,0)
406 g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_PRIORITY]);
408 g_object_notify (G_OBJECT (object), "priority");
414 * ges_track_object_set_active:
415 * @object: a #GESTrackObject
416 * @active: visibility
418 * Sets the usage of the @object. If @active is %TRUE, the object will be used for
419 * playback and rendering, else it will be ignored.
421 * Returns: %TRUE if the property was toggled, else %FALSE
424 ges_track_object_set_active (GESTrackObject * object, gboolean active)
426 GST_DEBUG ("object:%p, active:%d", object, active);
428 if (object->priv->gnlobject != NULL) {
429 if (G_UNLIKELY (active == object->active))
432 g_object_set (object->priv->gnlobject, "active", active, NULL);
434 object->priv->pending_active = active;
438 /* Callbacks from the GNonLin object */
440 gnlobject_start_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
441 GESTrackObject * obj)
444 GESTrackObjectClass *klass;
446 klass = GES_TRACK_OBJECT_GET_CLASS (obj);
448 g_object_get (gnlobject, "start", &start, NULL);
450 GST_DEBUG ("gnlobject start : %" GST_TIME_FORMAT " current : %"
451 GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (obj->start));
453 if (start != obj->start) {
455 if (klass->start_changed)
456 klass->start_changed (obj, start);
460 /* Callbacks from the GNonLin object */
462 gnlobject_media_start_cb (GstElement * gnlobject,
463 GParamSpec * arg G_GNUC_UNUSED, GESTrackObject * obj)
466 GESTrackObjectClass *klass;
468 klass = GES_TRACK_OBJECT_GET_CLASS (obj);
470 g_object_get (gnlobject, "media-start", &start, NULL);
472 GST_DEBUG ("gnlobject in-point : %" GST_TIME_FORMAT " current : %"
473 GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (obj->inpoint));
475 if (start != obj->inpoint) {
476 obj->inpoint = start;
477 if (klass->media_start_changed)
478 klass->media_start_changed (obj, start);
483 gnlobject_priority_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
484 GESTrackObject * obj)
487 GESTrackObjectClass *klass;
489 klass = GES_TRACK_OBJECT_GET_CLASS (obj);
491 g_object_get (gnlobject, "priority", &priority, NULL);
493 GST_DEBUG ("gnlobject priority : %d current : %d", priority, obj->priority);
495 if (priority != obj->priority) {
496 obj->priority = priority;
497 if (klass->gnl_priority_changed)
498 klass->gnl_priority_changed (obj, priority);
503 gnlobject_duration_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
504 GESTrackObject * obj)
507 GESTrackObjectClass *klass;
509 klass = GES_TRACK_OBJECT_GET_CLASS (obj);
511 g_object_get (gnlobject, "duration", &duration, NULL);
513 GST_DEBUG ("gnlobject duration : %" GST_TIME_FORMAT " current : %"
514 GST_TIME_FORMAT, GST_TIME_ARGS (duration), GST_TIME_ARGS (obj->duration));
516 if (duration != obj->duration) {
517 obj->duration = duration;
518 if (klass->duration_changed)
519 klass->duration_changed (obj, duration);
524 gnlobject_active_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
525 GESTrackObject * obj)
528 GESTrackObjectClass *klass;
530 klass = GES_TRACK_OBJECT_GET_CLASS (obj);
532 g_object_get (gnlobject, "active", &active, NULL);
534 GST_DEBUG ("gnlobject active : %d current : %d", active, obj->active);
536 if (active != obj->active) {
537 obj->active = active;
538 if (klass->active_changed)
539 klass->active_changed (obj, active);
544 /* default 'create_gnl_object' virtual method implementation */
546 ges_track_object_create_gnl_object_func (GESTrackObject * self)
548 GESTrackObjectClass *klass = NULL;
549 GstElement *child = NULL;
550 GstElement *gnlobject;
552 klass = GES_TRACK_OBJECT_GET_CLASS (self);
554 if (G_UNLIKELY (self->priv->gnlobject != NULL))
555 goto already_have_gnlobject;
557 if (G_UNLIKELY (klass->gnlobject_factorytype == NULL))
560 GST_DEBUG ("Creating a supporting gnlobject of type '%s'",
561 klass->gnlobject_factorytype);
563 gnlobject = gst_element_factory_make (klass->gnlobject_factorytype, NULL);
565 if (G_UNLIKELY (gnlobject == NULL))
568 if (klass->create_element) {
569 GST_DEBUG ("Calling subclass 'create_element' vmethod");
570 child = klass->create_element (self);
572 if (G_UNLIKELY (!child))
575 if (!gst_bin_add (GST_BIN (gnlobject), child))
578 GST_DEBUG ("Succesfully got the element to put in the gnlobject");
579 self->priv->element = child;
588 already_have_gnlobject:
590 GST_ERROR ("Already controlling a GnlObject %s",
591 GST_ELEMENT_NAME (self->priv->gnlobject));
597 GST_ERROR ("No GESTrackObject::gnlobject_factorytype implementation!");
603 GST_ERROR ("Error creating a gnlobject of type '%s'",
604 klass->gnlobject_factorytype);
610 GST_ERROR ("create_element returned NULL");
611 gst_object_unref (gnlobject);
617 GST_ERROR ("Error adding the contents to the gnlobject");
618 gst_object_unref (child);
619 gst_object_unref (gnlobject);
625 ensure_gnl_object (GESTrackObject * object)
627 GESTrackObjectClass *class;
628 GstElement *gnlobject;
629 gboolean res = FALSE;
631 if (object->priv->gnlobject && object->priv->valid)
634 /* 1. Create the GnlObject */
635 GST_DEBUG ("Creating GnlObject");
637 class = GES_TRACK_OBJECT_GET_CLASS (object);
639 if (G_UNLIKELY (class->create_gnl_object == NULL)) {
640 GST_ERROR ("No 'create_gnl_object' implementation !");
644 GST_DEBUG ("Calling virtual method");
646 /* call the create_gnl_object virtual method */
647 gnlobject = class->create_gnl_object (object);
649 if (G_UNLIKELY (gnlobject == NULL)) {
651 ("'create_gnl_object' implementation returned TRUE but no GnlObject is available");
655 object->priv->gnlobject = gnlobject;
657 /* 2. Fill in the GnlObject */
659 GST_DEBUG ("Got a valid GnlObject, now filling it in");
662 ges_timeline_object_fill_track_object (object->priv->timelineobj,
663 object, object->priv->gnlobject);
665 /* Connect to property notifications */
666 /* FIXME : remember the signalids so we can remove them later on !!! */
667 g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::start",
668 G_CALLBACK (gnlobject_start_cb), object);
669 g_signal_connect (G_OBJECT (object->priv->gnlobject),
670 "notify::media-start", G_CALLBACK (gnlobject_media_start_cb), object);
671 g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::duration",
672 G_CALLBACK (gnlobject_duration_cb), object);
673 g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::priority",
674 G_CALLBACK (gnlobject_priority_cb), object);
675 g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::active",
676 G_CALLBACK (gnlobject_active_cb), object);
678 /* Set some properties on the GnlObject */
679 g_object_set (object->priv->gnlobject,
680 "caps", ges_track_get_caps (object->priv->track),
681 "duration", object->priv->pending_duration,
682 "media-duration", object->priv->pending_duration,
683 "start", object->priv->pending_start,
684 "media-start", object->priv->pending_inpoint,
685 "priority", object->priv->pending_priority,
686 "active", object->priv->pending_active, NULL);
692 object->priv->valid = res;
694 GST_DEBUG ("Returning res:%d", res);
701 ges_track_object_set_track (GESTrackObject * object, GESTrack * track)
703 GST_DEBUG ("object:%p, track:%p", object, track);
705 object->priv->track = track;
707 if (object->priv->track)
708 return ensure_gnl_object (object);
714 * ges_track_object_get_track:
715 * @object: a #GESTrackObject
717 * Returns: (transfer none): The #GESTrack to which this object belongs. Can be %NULL if it
718 * is not in any track
721 ges_track_object_get_track (GESTrackObject * object)
723 g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), NULL);
725 return object->priv->track;
730 ges_track_object_set_timeline_object (GESTrackObject * object,
731 GESTimelineObject * tlobj)
733 GST_DEBUG ("object:%p, timeline-object:%p", object, tlobj);
735 object->priv->timelineobj = tlobj;
739 * ges_track_object_get_timeline_object:
740 * @object: a #GESTrackObject
742 * Returns: (transfer none): the #GESTimelineObject which is controlling
746 ges_track_object_get_timeline_object (GESTrackObject * object)
748 g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), NULL);
750 return object->priv->timelineobj;
754 * ges_track_object_get_gnlobject:
755 * @object: a #GESTrackObject
757 * Returns: (transfer none): the GNonLin object this object is controlling.
760 ges_track_object_get_gnlobject (GESTrackObject * object)
762 return object->priv->gnlobject;
766 * ges_track_object_get_element:
767 * @object: a #GESTrackObject
769 * Returns: (transfer none): the #GstElement this track object is controlling
773 ges_track_object_get_element (GESTrackObject * object)
775 return object->priv->element;
779 * ges_track_object_set_locked:
780 * @object: a #GESTrackObject
781 * @locked: whether the object is lock to its parent
783 * Set the locking status of the @object in relationship to its controlling
784 * #GESTimelineObject. If @locked is %TRUE, then this object will move synchronously
785 * with its controlling #GESTimelineObject.
788 ges_track_object_set_locked (GESTrackObject * object, gboolean locked)
790 object->priv->locked = locked;
794 * ges_track_object_is_locked:
795 * @object: a #GESTrackObject
797 * Returns: %TRUE if the object is moving synchronously to its controlling
798 * #GESTimelineObject, else %FALSE.
801 ges_track_object_is_locked (GESTrackObject * object)
803 return object->priv->locked;