GESTrack: Make more properties private
[platform/upstream/gstreamer.git] / ges / ges-track.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
23  * @short_description: Composition of objects
24  *
25  * Corresponds to one output format (i.e. audio OR video).
26  *
27  * Contains the compatible TrackObject(s).
28  *
29  * Wraps GNonLin's 'gnlcomposition' element.
30  */
31
32 #include "ges-internal.h"
33 #include "ges-track.h"
34 #include "ges-track-object.h"
35
36 G_DEFINE_TYPE (GESTrack, ges_track, GST_TYPE_BIN);
37
38 struct _GESTrackPrivate
39 {
40   /*< private > */
41   GESTimeline *timeline;
42   GList *trackobjects;
43
44   GstCaps *caps;
45
46   GstElement *composition;      /* The composition associated with this track */
47   GstPad *srcpad;               /* The source GhostPad */
48 };
49
50 enum
51 {
52   ARG_0,
53   ARG_CAPS,
54   ARG_TYPE
55 };
56
57 static void pad_added_cb (GstElement * element, GstPad * pad, GESTrack * track);
58
59 static void
60 pad_removed_cb (GstElement * element, GstPad * pad, GESTrack * track);
61
62 static void
63 ges_track_get_property (GObject * object, guint property_id,
64     GValue * value, GParamSpec * pspec)
65 {
66   GESTrack *track = GES_TRACK (object);
67
68   switch (property_id) {
69     case ARG_CAPS:
70       gst_value_set_caps (value, track->priv->caps);
71       break;
72     case ARG_TYPE:
73       g_value_set_flags (value, track->type);
74       break;
75     default:
76       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
77   }
78 }
79
80 static void
81 ges_track_set_property (GObject * object, guint property_id,
82     const GValue * value, GParamSpec * pspec)
83 {
84   GESTrack *track = GES_TRACK (object);
85
86   switch (property_id) {
87     case ARG_CAPS:
88       ges_track_set_caps (track, gst_value_get_caps (value));
89       break;
90     case ARG_TYPE:
91       track->type = g_value_get_flags (value);
92       break;
93     default:
94       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
95   }
96 }
97
98 static void
99 ges_track_dispose (GObject * object)
100 {
101   GESTrack *track = (GESTrack *) object;
102   GESTrackPrivate *priv = track->priv;
103
104   while (priv->trackobjects) {
105     GESTrackObject *trobj = GES_TRACK_OBJECT (priv->trackobjects->data);
106     ges_track_remove_object (track, trobj);
107     ges_timeline_object_release_track_object (trobj->timelineobj, trobj);
108   }
109
110   if (priv->composition) {
111     gst_bin_remove (GST_BIN (object), priv->composition);
112     priv->composition = NULL;
113   }
114
115   if (priv->caps) {
116     gst_caps_unref (priv->caps);
117     priv->caps = NULL;
118   }
119
120   G_OBJECT_CLASS (ges_track_parent_class)->dispose (object);
121 }
122
123 static void
124 ges_track_finalize (GObject * object)
125 {
126   G_OBJECT_CLASS (ges_track_parent_class)->finalize (object);
127 }
128
129 static void
130 ges_track_class_init (GESTrackClass * klass)
131 {
132   GObjectClass *object_class = G_OBJECT_CLASS (klass);
133
134   g_type_class_add_private (klass, sizeof (GESTrackPrivate));
135
136   object_class->get_property = ges_track_get_property;
137   object_class->set_property = ges_track_set_property;
138   object_class->dispose = ges_track_dispose;
139   object_class->finalize = ges_track_finalize;
140
141   /**
142    * GESTrack:caps
143    *
144    * Caps used to filter/choose the output stream. This is generally set to
145    * a generic set of caps like 'video/x-raw-rgb;video/x-raw-yuv' for raw video.
146    *
147    * Default value: #GST_CAPS_ANY.
148    */
149   g_object_class_install_property (object_class, ARG_CAPS,
150       g_param_spec_boxed ("caps", "Caps",
151           "Caps used to filter/choose the output stream",
152           GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
153
154   /**
155    * GESTrack:track-type
156    *
157    * Type of stream the track outputs. This is used when creating the #GESTrack
158    * to specify in generic terms what type of content will be outputted.
159    *
160    * It also serves as a 'fast' way to check what type of data will be outputted
161    * from the #GESTrack without having to actually check the #GESTrack's caps
162    * property.
163    */
164   g_object_class_install_property (object_class, ARG_TYPE,
165       g_param_spec_flags ("track-type", "TrackType",
166           "Type of stream the track outputs",
167           GES_TYPE_TRACK_TYPE, GES_TRACK_TYPE_CUSTOM,
168           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
169 }
170
171 static void
172 ges_track_init (GESTrack * self)
173 {
174   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
175       GES_TYPE_TRACK, GESTrackPrivate);
176
177   self->priv->composition = gst_element_factory_make ("gnlcomposition", NULL);
178
179   g_signal_connect (self->priv->composition, "pad-added",
180       (GCallback) pad_added_cb, self);
181   g_signal_connect (self->priv->composition, "pad-removed",
182       (GCallback) pad_removed_cb, self);
183
184   if (!gst_bin_add (GST_BIN (self), self->priv->composition))
185     GST_ERROR ("Couldn't add composition to bin !");
186 }
187
188 /**
189  * ges_track_new:
190  * @type: The type of track
191  * @caps: The caps to restrict the output of the track to.
192  *
193  * Creates a new #GESTrack with the given @type and @caps.
194  *
195  * The newly created track will steal a reference to the caps. If you wish to 
196  * use those caps elsewhere, you will have to take an extra reference.
197  *
198  * Returns: A new #GESTrack.
199  */
200 GESTrack *
201 ges_track_new (GESTrackType type, GstCaps * caps)
202 {
203   GESTrack *track;
204
205   track = g_object_new (GES_TYPE_TRACK, "caps", caps, "track-type", type, NULL);
206   gst_caps_unref (caps);
207
208   return track;
209 }
210
211 /**
212  * ges_track_video_raw_new:
213  *
214  * Creates a new #GESTrack of type #GES_TRACK_TYPE_VIDEO and with generic
215  * raw video caps ("video/x-raw-yuv;video/x-raw-rgb");
216  *
217  * Returns: A new #GESTrack.
218  */
219 GESTrack *
220 ges_track_video_raw_new (void)
221 {
222   GESTrack *track;
223   GstCaps *caps = gst_caps_from_string ("video/x-raw-yuv;video/x-raw-rgb");
224
225   track = ges_track_new (GES_TRACK_TYPE_VIDEO, caps);
226
227   return track;
228 }
229
230 /**
231  * ges_track_audio_raw_new:
232  *
233  * Creates a new #GESTrack of type #GES_TRACK_TYPE_AUDIO and with generic
234  * raw audio caps ("audio/x-raw-int;audio/x-raw-float");
235  *
236  * Returns: A new #GESTrack.
237  */
238 GESTrack *
239 ges_track_audio_raw_new (void)
240 {
241   GESTrack *track;
242   GstCaps *caps = gst_caps_from_string ("audio/x-raw-int;audio/x-raw-float");
243
244   track = ges_track_new (GES_TRACK_TYPE_AUDIO, caps);
245
246   return track;
247 }
248
249 /**
250  * ges_track_set_timeline:
251  * @track: a #GESTrack
252  * @timeline: a #GESTimeline
253  *
254  * Sets @timeline as the timeline controlling @track.
255  */
256 void
257 ges_track_set_timeline (GESTrack * track, GESTimeline * timeline)
258 {
259   GST_DEBUG ("track:%p, timeline:%p", track, timeline);
260
261   track->priv->timeline = timeline;
262 }
263
264 /**
265  * ges_track_set_caps:
266  * @track: a #GESTrack
267  * @caps: the #GstCaps to set
268  *
269  * Sets the given @caps on the track.
270  */
271 void
272 ges_track_set_caps (GESTrack * track, const GstCaps * caps)
273 {
274   GESTrackPrivate *priv;
275
276   g_return_if_fail (GES_IS_TRACK (track));
277   g_return_if_fail (GST_IS_CAPS (caps));
278
279   GST_DEBUG ("track:%p, caps:%" GST_PTR_FORMAT, track, caps);
280
281   priv = track->priv;
282
283   if (priv->caps)
284     gst_caps_unref (priv->caps);
285   priv->caps = gst_caps_copy (caps);
286
287   g_object_set (priv->composition, "caps", caps, NULL);
288   /* FIXME : update all trackobjects ? */
289 }
290
291 /**
292  * ges_track_add_object:
293  * @track: a #GESTrack
294  * @object: the #GESTrackObject to add
295  *
296  * Adds the given object to the track.
297  *
298  * Returns: #TRUE if the object was properly added. #FALSE if the track does not
299  * want to accept the object.
300  */
301 gboolean
302 ges_track_add_object (GESTrack * track, GESTrackObject * object)
303 {
304   g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
305   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), FALSE);
306
307   GST_DEBUG ("track:%p, object:%p", track, object);
308
309   if (G_UNLIKELY (object->track != NULL)) {
310     GST_WARNING ("Object already belongs to another track");
311     return FALSE;
312   }
313
314   if (G_UNLIKELY (object->gnlobject != NULL)) {
315     GST_ERROR ("TrackObject doesn't have a gnlobject !");
316     return FALSE;
317   }
318
319   if (G_UNLIKELY (!ges_track_object_set_track (object, track))) {
320     GST_ERROR ("Couldn't properly add the object to the Track");
321     return FALSE;
322   }
323
324   GST_DEBUG ("Adding object to ourself");
325
326   /* make sure the object has a valid gnlobject ! */
327   if (G_UNLIKELY (!gst_bin_add (GST_BIN (track->priv->composition),
328               object->gnlobject))) {
329     GST_WARNING ("Couldn't add object to the GnlComposition");
330     return FALSE;
331   }
332
333   track->priv->trackobjects = g_list_append (track->priv->trackobjects, object);
334
335   return TRUE;
336 }
337
338 /**
339  * ges_track_remove_object:
340  * @track: a #GESTrack
341  * @object: the #GESTrackObject to remove
342  *
343  * Removes the object from the track.
344  *
345  * Returns: #TRUE if the object was removed, else #FALSE if the track
346  * could not remove the object (like if it didn't belong to the track).
347  */
348 gboolean
349 ges_track_remove_object (GESTrack * track, GESTrackObject * object)
350 {
351   GESTrackPrivate *priv;
352
353   g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
354   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), FALSE);
355
356   GST_DEBUG ("track:%p, object:%p", track, object);
357
358   priv = track->priv;
359
360   if (G_UNLIKELY (object->track != track)) {
361     GST_WARNING ("Object belongs to another track");
362     return FALSE;
363   }
364
365   if (G_LIKELY (object->gnlobject != NULL)) {
366     GST_DEBUG ("Removing GnlObject from composition");
367     if (!gst_bin_remove (GST_BIN (priv->composition), object->gnlobject)) {
368       GST_WARNING ("Failed to remove gnlobject from composition");
369       return FALSE;
370     }
371   }
372
373   ges_track_object_set_track (object, NULL);
374   priv->trackobjects = g_list_remove (priv->trackobjects, object);
375
376   return TRUE;
377 }
378
379 static void
380 pad_added_cb (GstElement * element, GstPad * pad, GESTrack * track)
381 {
382   GESTrackPrivate *priv = track->priv;
383
384   GST_DEBUG ("track:%p, pad %s:%s", track, GST_DEBUG_PAD_NAME (pad));
385
386   /* ghost the pad */
387   priv->srcpad = gst_ghost_pad_new ("src", pad);
388
389   gst_pad_set_active (priv->srcpad, TRUE);
390
391   gst_element_add_pad (GST_ELEMENT (track), priv->srcpad);
392
393   GST_DEBUG ("done");
394 }
395
396 static void
397 pad_removed_cb (GstElement * element, GstPad * pad, GESTrack * track)
398 {
399   GESTrackPrivate *priv = track->priv;
400
401   GST_DEBUG ("track:%p, pad %s:%s", track, GST_DEBUG_PAD_NAME (pad));
402
403   if (G_LIKELY (priv->srcpad)) {
404     gst_pad_set_active (priv->srcpad, FALSE);
405     gst_element_remove_pad (GST_ELEMENT (track), priv->srcpad);
406     priv->srcpad = NULL;
407   }
408
409   GST_DEBUG ("done");
410 }
411
412 /**
413  * ges_track_get_caps:
414  * @track: a #GESTrack
415  *
416  * Returns: The #GstCaps this track is configured to output.
417  */
418 const GstCaps *
419 ges_track_get_caps (GESTrack * track)
420 {
421   g_return_val_if_fail (GES_IS_TRACK (track), NULL);
422
423   return track->priv->caps;
424 }
425
426 /**
427  * ges_track_get_timeline:
428  * @track: a #GESTrack
429  *
430  * Returns: The #GESTimeline this track belongs to. Can be %NULL.
431  */
432 const GESTimeline *
433 ges_track_get_timeline (GESTrack * track)
434 {
435   g_return_val_if_fail (GES_IS_TRACK (track), NULL);
436
437   return track->priv->timeline;
438 }