GESTrackObject: Store pending values when GnlObject isn't created yet
[platform/upstream/gstreamer.git] / ges / ges-track-object.c
1 /* GStreamer Editing Services
2  * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
3  *               2009 Nokia Corporation
4  *
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.
9  *
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.
14  *
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.
19  */
20
21 /**
22  * SECTION:ges-track-object
23  * @short_description: Base Class for objects contained in a #GESTrack
24  *
25  * #GESTrackObject is the Base Class for any object that can be contained in a
26  * #GESTrack.
27  *
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
30  * priority.
31  */
32
33 #include "ges-internal.h"
34 #include "ges-track-object.h"
35 #include "ges-timeline-object.h"
36
37 static GQuark _start_quark;
38 static GQuark _inpoint_quark;
39 static GQuark _duration_quark;
40 static GQuark _priority_quark;
41
42 #define _do_init \
43 { \
44   _start_quark = g_quark_from_static_string ("start"); \
45   _inpoint_quark = g_quark_from_static_string ("inpoint"); \
46   _duration_quark = g_quark_from_static_string ("duration"); \
47   _priority_quark = g_quark_from_static_string ("priority"); \
48 }
49
50 G_DEFINE_TYPE_WITH_CODE (GESTrackObject, ges_track_object, G_TYPE_OBJECT,
51     _do_init);
52
53 enum
54 {
55   PROP_0,
56   PROP_START,
57   PROP_INPOINT,
58   PROP_DURATION,
59   PROP_PRIORITY,
60   PROP_ACTIVE
61 };
62
63 static gboolean
64 ges_track_object_create_gnl_object_func (GESTrackObject * object);
65
66 static void
67 ges_track_object_get_property (GObject * object, guint property_id,
68     GValue * value, GParamSpec * pspec)
69 {
70   GESTrackObject *tobj = GES_TRACK_OBJECT (object);
71
72   switch (property_id) {
73     case PROP_START:
74       g_value_set_uint64 (value, tobj->start);
75       break;
76     case PROP_INPOINT:
77       g_value_set_uint64 (value, tobj->inpoint);
78       break;
79     case PROP_DURATION:
80       g_value_set_uint64 (value, tobj->duration);
81       break;
82     case PROP_PRIORITY:
83       g_value_set_uint (value, tobj->priority);
84       break;
85     case PROP_ACTIVE:
86       g_value_set_boolean (value, tobj->active);
87       break;
88     default:
89       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
90   }
91 }
92
93 static void
94 ges_track_object_set_property (GObject * object, guint property_id,
95     const GValue * value, GParamSpec * pspec)
96 {
97   GESTrackObject *tobj = GES_TRACK_OBJECT (object);
98
99   switch (property_id) {
100     case PROP_START:
101       ges_track_object_set_start_internal (tobj, g_value_get_uint64 (value));
102       break;
103     case PROP_INPOINT:
104       ges_track_object_set_inpoint_internal (tobj, g_value_get_uint64 (value));
105       break;
106     case PROP_DURATION:
107       ges_track_object_set_duration_internal (tobj, g_value_get_uint64 (value));
108       break;
109     case PROP_PRIORITY:
110       ges_track_object_set_priority_internal (tobj, g_value_get_uint (value));
111       break;
112     case PROP_ACTIVE:
113       ges_track_object_set_active (tobj, g_value_get_boolean (value));
114       break;
115     default:
116       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
117   }
118 }
119
120 static void
121 ges_track_object_dispose (GObject * object)
122 {
123   G_OBJECT_CLASS (ges_track_object_parent_class)->dispose (object);
124 }
125
126 static void
127 ges_track_object_finalize (GObject * object)
128 {
129   G_OBJECT_CLASS (ges_track_object_parent_class)->finalize (object);
130 }
131
132 static void
133 ges_track_object_class_init (GESTrackObjectClass * klass)
134 {
135   GObjectClass *object_class = G_OBJECT_CLASS (klass);
136
137   object_class->get_property = ges_track_object_get_property;
138   object_class->set_property = ges_track_object_set_property;
139   object_class->dispose = ges_track_object_dispose;
140   object_class->finalize = ges_track_object_finalize;
141
142   /**
143    * GESTrackObject:start
144    *
145    * The position of the object in the container #GESTrack (in nanoseconds).
146    */
147   g_object_class_install_property (object_class, PROP_START,
148       g_param_spec_uint64 ("start", "Start",
149           "The position in the container", 0, G_MAXUINT64, 0,
150           G_PARAM_READWRITE));
151
152   /**
153    * GESTrackObject:in-point
154    *
155    * The in-point at which this #GESTrackObject will start outputting data
156    * from its contents (in nanoseconds).
157    *
158    * Ex : an in-point of 5 seconds means that the first outputted buffer will
159    * be the one located 5 seconds in the controlled resource.
160    */
161   g_object_class_install_property (object_class, PROP_INPOINT,
162       g_param_spec_uint64 ("in-point", "In-point", "The in-point", 0,
163           G_MAXUINT64, 0, G_PARAM_READWRITE));
164
165   /**
166    * GESTrackObject:duration
167    *
168    * The duration (in nanoseconds) which will be used in the container #GESTrack
169    * starting from 'in-point'.
170    *
171    */
172   g_object_class_install_property (object_class, PROP_DURATION,
173       g_param_spec_uint64 ("duration", "Duration", "The duration to use",
174           0, G_MAXUINT64, GST_SECOND, G_PARAM_READWRITE));
175
176   /**
177    * GESTrackObject:priority
178    *
179    * The priority of the object within the containing #GESTrack.
180    * If two objects intersect over the same region of time, the @priority
181    * property is used to decide which one takes precedence.
182    *
183    * The highest priority (that supercedes everything) is 0, and then lowering
184    * priorities go in increasing numerical value (with #G_MAXUINT64 being the
185    * lowest priority).
186    */
187   g_object_class_install_property (object_class, PROP_PRIORITY,
188       g_param_spec_uint ("priority", "Priority",
189           "The priority of the object", 0, G_MAXUINT, 0, G_PARAM_READWRITE));
190
191   /**
192    * GESTrackObject:active
193    *
194    * Whether the object should be taken into account in the #GEStrack output.
195    * If #FALSE, then its contents will not be used in the resulting track.
196    */
197   g_object_class_install_property (object_class, PROP_ACTIVE,
198       g_param_spec_boolean ("active", "Active", "Use object in output",
199           TRUE, G_PARAM_READWRITE));
200
201   klass->create_gnl_object = ges_track_object_create_gnl_object_func;
202 }
203
204 static void
205 ges_track_object_init (GESTrackObject * self)
206 {
207   /* Sane default values */
208   self->pending_start = 0;
209   self->pending_inpoint = 0;
210   self->pending_duration = GST_SECOND;
211   self->pending_priority = 1;
212   self->pending_active = TRUE;
213 }
214
215 gboolean
216 ges_track_object_set_start_internal (GESTrackObject * object, guint64 start)
217 {
218   GST_DEBUG ("object:%p, start:%" GST_TIME_FORMAT,
219       object, GST_TIME_ARGS (start));
220
221   if (object->gnlobject != NULL) {
222     if (G_UNLIKELY (start == object->start))
223       return FALSE;
224
225     g_object_set (object->gnlobject, "start", start, NULL);
226   } else
227     object->pending_start = start;
228   return TRUE;
229 };
230
231 gboolean
232 ges_track_object_set_inpoint_internal (GESTrackObject * object, guint64 inpoint)
233 {
234
235   GST_DEBUG ("object:%p, inpoint:%" GST_TIME_FORMAT,
236       object, GST_TIME_ARGS (inpoint));
237
238   if (object->gnlobject != NULL) {
239     if (G_UNLIKELY (inpoint == object->inpoint))
240       return FALSE;
241
242     g_object_set (object->gnlobject, "media-start", inpoint, NULL);
243   } else
244     object->pending_inpoint = inpoint;
245
246   return TRUE;
247 }
248
249 gboolean
250 ges_track_object_set_duration_internal (GESTrackObject * object,
251     guint64 duration)
252 {
253   GST_DEBUG ("object:%p, duration:%" GST_TIME_FORMAT,
254       object, GST_TIME_ARGS (duration));
255
256   if (object->gnlobject != NULL) {
257     if (G_UNLIKELY (duration == object->duration))
258       return FALSE;
259
260     g_object_set (object->gnlobject, "duration", duration, "media-duration",
261         duration, NULL);
262   } else
263     object->pending_duration = duration;
264   return TRUE;
265 }
266
267 gboolean
268 ges_track_object_set_priority_internal (GESTrackObject * object,
269     guint32 priority)
270 {
271   GST_DEBUG ("object:%p, priority:%d", object, priority);
272
273   if (object->gnlobject != NULL) {
274     if (G_UNLIKELY (priority == object->priority))
275       return FALSE;
276
277     g_object_set (object->gnlobject, "priority", priority, NULL);
278   } else
279     object->pending_priority = priority;
280   return TRUE;
281 }
282
283 gboolean
284 ges_track_object_set_active (GESTrackObject * object, gboolean active)
285 {
286   GST_DEBUG ("object:%p, active:%d", object, active);
287
288   if (object->gnlobject != NULL) {
289     if (G_UNLIKELY (active == object->active))
290       return FALSE;
291
292     g_object_set (object->gnlobject, "active", active, NULL);
293   } else
294     object->pending_active = active;
295   return TRUE;
296 }
297
298 /* Callbacks from the GNonLin object */
299 void
300 gnlobject_start_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
301     GESTrackObject * obj)
302 {
303   guint64 start;
304   g_object_get (gnlobject, "start", &start, NULL);
305
306   GST_DEBUG ("gnlobject start : %" GST_TIME_FORMAT " current : %"
307       GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (obj->start));
308
309   if (start != obj->start) {
310     obj->start = start;
311     /* FIXME : emit changed */
312   }
313 }
314
315 /* Callbacks from the GNonLin object */
316 void
317 gnlobject_media_start_cb (GstElement * gnlobject,
318     GParamSpec * arg G_GNUC_UNUSED, GESTrackObject * obj)
319 {
320   guint64 start;
321   g_object_get (gnlobject, "media-start", &start, NULL);
322
323   GST_DEBUG ("gnlobject in-point : %" GST_TIME_FORMAT " current : %"
324       GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (obj->inpoint));
325
326   if (start != obj->inpoint) {
327     obj->inpoint = start;
328     /* FIXME : emit changed */
329   }
330 }
331
332 void
333 gnlobject_priority_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
334     GESTrackObject * obj)
335 {
336   guint32 priority;
337   g_object_get (gnlobject, "priority", &priority, NULL);
338
339   GST_DEBUG ("gnlobject priority : %d current : %d", priority, obj->priority);
340
341   if (priority != obj->priority) {
342     obj->priority = priority;
343     /* FIXME : emit changed */
344   }
345 }
346
347 void
348 gnlobject_duration_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
349     GESTrackObject * obj)
350 {
351   guint64 duration;
352   g_object_get (gnlobject, "duration", &duration, NULL);
353
354   GST_DEBUG ("gnlobject duration : %" GST_TIME_FORMAT " current : %"
355       GST_TIME_FORMAT, GST_TIME_ARGS (duration), GST_TIME_ARGS (obj->duration));
356
357   if (duration != obj->duration) {
358     obj->duration = duration;
359     /* FIXME : emit changed */
360   }
361 }
362
363 void
364 gnlobject_active_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
365     GESTrackObject * obj)
366 {
367   gboolean active;
368   g_object_get (gnlobject, "active", &active, NULL);
369
370   GST_DEBUG ("gnlobject active : %d current : %d", active, obj->active);
371
372   if (active != obj->active) {
373     obj->active = active;
374     /* FIXME : emit changed */
375   }
376 }
377
378
379 /* default 'create_gnl_object' virtual method implementation */
380 static gboolean
381 ges_track_object_create_gnl_object_func (GESTrackObject * object)
382 {
383
384   return FALSE;
385 }
386
387 static gboolean
388 ensure_gnl_object (GESTrackObject * object)
389 {
390   GESTrackObjectClass *class;
391   gboolean res;
392
393   if (object->gnlobject && object->valid)
394     return TRUE;
395
396   /* 1. Create the GnlObject */
397   GST_DEBUG ("Creating GnlObject");
398
399   class = GES_TRACK_OBJECT_GET_CLASS (object);
400
401   if (G_UNLIKELY (class->create_gnl_object == NULL)) {
402     GST_ERROR ("No 'create_gnl_object' implementation !");
403     return FALSE;
404   }
405
406   GST_DEBUG ("Calling virtual method");
407
408   /* call the create_gnl_object virtual method */
409   res = class->create_gnl_object (object);
410
411   if (G_UNLIKELY (res && (object->gnlobject == NULL))) {
412     GST_ERROR
413         ("'create_gnl_object' implementation returned TRUE but no GnlObject is available");
414     return FALSE;
415   }
416
417   /* Connect to property notifications */
418   g_signal_connect (G_OBJECT (object->gnlobject), "notify::start",
419       G_CALLBACK (gnlobject_start_cb), object);
420   g_signal_connect (G_OBJECT (object->gnlobject), "notify::media-start",
421       G_CALLBACK (gnlobject_media_start_cb), object);
422   g_signal_connect (G_OBJECT (object->gnlobject), "notify::duration",
423       G_CALLBACK (gnlobject_duration_cb), object);
424   g_signal_connect (G_OBJECT (object->gnlobject), "notify::priority",
425       G_CALLBACK (gnlobject_priority_cb), object);
426   g_signal_connect (G_OBJECT (object->gnlobject), "notify::active",
427       G_CALLBACK (gnlobject_active_cb), object);
428
429   /* 2. Fill in the GnlObject */
430   if (res) {
431     GST_DEBUG ("Got a valid GnlObject, now filling it in");
432
433     res =
434         ges_timeline_object_fill_track_object (object->timelineobj, object,
435         object->gnlobject);
436     if (res) {
437       /* Set some properties on the GnlObject */
438       g_object_set (object->gnlobject,
439           "caps", object->track->caps,
440           "duration", object->pending_duration,
441           "media-duration", object->pending_duration,
442           "start", object->pending_start,
443           "media-start", object->pending_inpoint,
444           "priority", object->pending_priority,
445           "active", object->pending_active, NULL);
446
447     }
448   }
449
450   object->valid = res;
451
452   GST_DEBUG ("Returning res:%d", res);
453
454   return res;
455 }
456
457 gboolean
458 ges_track_object_set_track (GESTrackObject * object, GESTrack * track)
459 {
460   GST_DEBUG ("object:%p, track:%p", object, track);
461
462   object->track = track;
463
464   if (object->track)
465     return ensure_gnl_object (object);
466
467   return TRUE;
468 }
469
470 void
471 ges_track_object_set_timeline_object (GESTrackObject * object,
472     GESTimelineObject * tlobj)
473 {
474   GST_DEBUG ("object:%p, timeline-object:%p", object, tlobj);
475
476   object->timelineobj = tlobj;
477 }