GESTrack: Make debug statement more useful
[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 ((GESTimelineObject *)
108         ges_track_object_get_timeline_object (trobj), trobj);
109   }
110
111   if (priv->composition) {
112     gst_bin_remove (GST_BIN (object), priv->composition);
113     priv->composition = NULL;
114   }
115
116   if (priv->caps) {
117     gst_caps_unref (priv->caps);
118     priv->caps = NULL;
119   }
120
121   G_OBJECT_CLASS (ges_track_parent_class)->dispose (object);
122 }
123
124 static void
125 ges_track_finalize (GObject * object)
126 {
127   G_OBJECT_CLASS (ges_track_parent_class)->finalize (object);
128 }
129
130 static void
131 ges_track_class_init (GESTrackClass * klass)
132 {
133   GObjectClass *object_class = G_OBJECT_CLASS (klass);
134
135   g_type_class_add_private (klass, sizeof (GESTrackPrivate));
136
137   object_class->get_property = ges_track_get_property;
138   object_class->set_property = ges_track_set_property;
139   object_class->dispose = ges_track_dispose;
140   object_class->finalize = ges_track_finalize;
141
142   /**
143    * GESTrack:caps
144    *
145    * Caps used to filter/choose the output stream. This is generally set to
146    * a generic set of caps like 'video/x-raw-rgb;video/x-raw-yuv' for raw video.
147    *
148    * Default value: #GST_CAPS_ANY.
149    */
150   g_object_class_install_property (object_class, ARG_CAPS,
151       g_param_spec_boxed ("caps", "Caps",
152           "Caps used to filter/choose the output stream",
153           GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
154
155   /**
156    * GESTrack:track-type
157    *
158    * Type of stream the track outputs. This is used when creating the #GESTrack
159    * to specify in generic terms what type of content will be outputted.
160    *
161    * It also serves as a 'fast' way to check what type of data will be outputted
162    * from the #GESTrack without having to actually check the #GESTrack's caps
163    * property.
164    */
165   g_object_class_install_property (object_class, ARG_TYPE,
166       g_param_spec_flags ("track-type", "TrackType",
167           "Type of stream the track outputs",
168           GES_TYPE_TRACK_TYPE, GES_TRACK_TYPE_CUSTOM,
169           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
170 }
171
172 static void
173 ges_track_init (GESTrack * self)
174 {
175   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
176       GES_TYPE_TRACK, GESTrackPrivate);
177
178   self->priv->composition = gst_element_factory_make ("gnlcomposition", NULL);
179
180   g_signal_connect (self->priv->composition, "pad-added",
181       (GCallback) pad_added_cb, self);
182   g_signal_connect (self->priv->composition, "pad-removed",
183       (GCallback) pad_removed_cb, self);
184
185   if (!gst_bin_add (GST_BIN (self), self->priv->composition))
186     GST_ERROR ("Couldn't add composition to bin !");
187 }
188
189 /**
190  * ges_track_new:
191  * @type: The type of track
192  * @caps: The caps to restrict the output of the track to.
193  *
194  * Creates a new #GESTrack with the given @type and @caps.
195  *
196  * The newly created track will steal a reference to the caps. If you wish to 
197  * use those caps elsewhere, you will have to take an extra reference.
198  *
199  * Returns: A new #GESTrack.
200  */
201 GESTrack *
202 ges_track_new (GESTrackType type, GstCaps * caps)
203 {
204   GESTrack *track;
205
206   track = g_object_new (GES_TYPE_TRACK, "caps", caps, "track-type", type, NULL);
207   gst_caps_unref (caps);
208
209   return track;
210 }
211
212 /**
213  * ges_track_video_raw_new:
214  *
215  * Creates a new #GESTrack of type #GES_TRACK_TYPE_VIDEO and with generic
216  * raw video caps ("video/x-raw-yuv;video/x-raw-rgb");
217  *
218  * Returns: A new #GESTrack.
219  */
220 GESTrack *
221 ges_track_video_raw_new (void)
222 {
223   GESTrack *track;
224   GstCaps *caps = gst_caps_from_string ("video/x-raw-yuv;video/x-raw-rgb");
225
226   track = ges_track_new (GES_TRACK_TYPE_VIDEO, caps);
227
228   return track;
229 }
230
231 /**
232  * ges_track_audio_raw_new:
233  *
234  * Creates a new #GESTrack of type #GES_TRACK_TYPE_AUDIO and with generic
235  * raw audio caps ("audio/x-raw-int;audio/x-raw-float");
236  *
237  * Returns: A new #GESTrack.
238  */
239 GESTrack *
240 ges_track_audio_raw_new (void)
241 {
242   GESTrack *track;
243   GstCaps *caps = gst_caps_from_string ("audio/x-raw-int;audio/x-raw-float");
244
245   track = ges_track_new (GES_TRACK_TYPE_AUDIO, caps);
246
247   return track;
248 }
249
250 /**
251  * ges_track_set_timeline:
252  * @track: a #GESTrack
253  * @timeline: a #GESTimeline
254  *
255  * Sets @timeline as the timeline controlling @track.
256  */
257 void
258 ges_track_set_timeline (GESTrack * track, GESTimeline * timeline)
259 {
260   GST_DEBUG ("track:%p, timeline:%p", track, timeline);
261
262   track->priv->timeline = timeline;
263 }
264
265 /**
266  * ges_track_set_caps:
267  * @track: a #GESTrack
268  * @caps: the #GstCaps to set
269  *
270  * Sets the given @caps on the track.
271  */
272 void
273 ges_track_set_caps (GESTrack * track, const GstCaps * caps)
274 {
275   GESTrackPrivate *priv;
276
277   g_return_if_fail (GES_IS_TRACK (track));
278   g_return_if_fail (GST_IS_CAPS (caps));
279
280   GST_DEBUG ("track:%p, caps:%" GST_PTR_FORMAT, track, caps);
281
282   priv = track->priv;
283
284   if (priv->caps)
285     gst_caps_unref (priv->caps);
286   priv->caps = gst_caps_copy (caps);
287
288   g_object_set (priv->composition, "caps", caps, NULL);
289   /* FIXME : update all trackobjects ? */
290 }
291
292 /**
293  * ges_track_add_object:
294  * @track: a #GESTrack
295  * @object: (transfer full): the #GESTrackObject to add
296  *
297  * Adds the given object to the track. Sets the object's controlling track,
298  * and thus takes ownership of the @object.
299  *
300  * An object can only be added to one track.
301  *
302  * Returns: #TRUE if the object was properly added. #FALSE if the track does not
303  * want to accept the object.
304  */
305 gboolean
306 ges_track_add_object (GESTrack * track, GESTrackObject * object)
307 {
308   g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
309   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), FALSE);
310
311   GST_DEBUG ("track:%p, object:%p", track, object);
312
313   if (G_UNLIKELY (ges_track_object_get_track (object) != NULL)) {
314     GST_WARNING ("Object already belongs to another track");
315     return FALSE;
316   }
317
318   /* At this point, the track object shouldn't have any gnlobject since
319    * it hasn't been added to a track yet.
320    * FIXME : This check seems a bit obsolete */
321   if (G_UNLIKELY (ges_track_object_get_gnlobject (object) != NULL)) {
322     GST_ERROR ("TrackObject already controls a gnlobject !");
323     return FALSE;
324   }
325
326   if (G_UNLIKELY (!ges_track_object_set_track (object, track))) {
327     GST_ERROR ("Couldn't properly add the object to the Track");
328     return FALSE;
329   }
330
331   GST_DEBUG ("Adding object to ourself");
332
333   if (G_UNLIKELY (!gst_bin_add (GST_BIN (track->priv->composition),
334               ges_track_object_get_gnlobject (object)))) {
335     GST_WARNING ("Couldn't add object to the GnlComposition");
336     return FALSE;
337   }
338
339   g_object_ref_sink (object);
340
341   track->priv->trackobjects = g_list_append (track->priv->trackobjects, object);
342
343   return TRUE;
344 }
345
346 /**
347  * ges_track_remove_object:
348  * @track: a #GESTrack
349  * @object: the #GESTrackObject to remove
350  *
351  * Removes the object from the track and unparents it.
352  * Unparenting it means the reference owned by @track on the @object will be
353  * removed. If you wish to use the @object after this function, make sure you
354  * call g_object_ref() before removing it from the @track.
355  *
356  * Returns: #TRUE if the object was removed, else #FALSE if the track
357  * could not remove the object (like if it didn't belong to the track).
358  */
359 gboolean
360 ges_track_remove_object (GESTrack * track, GESTrackObject * object)
361 {
362   GESTrackPrivate *priv;
363   GstElement *gnlobject;
364
365   g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
366   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), FALSE);
367
368   GST_DEBUG ("track:%p, object:%p", track, object);
369
370   priv = track->priv;
371
372   if (G_UNLIKELY (ges_track_object_get_track (object) != track)) {
373     GST_WARNING ("Object belongs to another track");
374     return FALSE;
375   }
376
377   if ((gnlobject = ges_track_object_get_gnlobject (object))) {
378     GST_DEBUG ("Removing GnlObject '%s' from composition '%s'",
379         GST_ELEMENT_NAME (gnlobject), GST_ELEMENT_NAME (priv->composition));
380     if (!gst_bin_remove (GST_BIN (priv->composition), gnlobject)) {
381       GST_WARNING ("Failed to remove gnlobject from composition");
382       return FALSE;
383     }
384   }
385
386   ges_track_object_set_track (object, NULL);
387   priv->trackobjects = g_list_remove (priv->trackobjects, object);
388
389   g_object_unref (object);
390
391   return TRUE;
392 }
393
394 static void
395 pad_added_cb (GstElement * element, GstPad * pad, GESTrack * track)
396 {
397   GESTrackPrivate *priv = track->priv;
398
399   GST_DEBUG ("track:%p, pad %s:%s", track, GST_DEBUG_PAD_NAME (pad));
400
401   /* ghost the pad */
402   priv->srcpad = gst_ghost_pad_new ("src", pad);
403
404   gst_pad_set_active (priv->srcpad, TRUE);
405
406   gst_element_add_pad (GST_ELEMENT (track), priv->srcpad);
407
408   GST_DEBUG ("done");
409 }
410
411 static void
412 pad_removed_cb (GstElement * element, GstPad * pad, GESTrack * track)
413 {
414   GESTrackPrivate *priv = track->priv;
415
416   GST_DEBUG ("track:%p, pad %s:%s", track, GST_DEBUG_PAD_NAME (pad));
417
418   if (G_LIKELY (priv->srcpad)) {
419     gst_pad_set_active (priv->srcpad, FALSE);
420     gst_element_remove_pad (GST_ELEMENT (track), priv->srcpad);
421     priv->srcpad = NULL;
422   }
423
424   GST_DEBUG ("done");
425 }
426
427 /**
428  * ges_track_get_caps:
429  * @track: a #GESTrack
430  *
431  * Get the #GstCaps this track is configured to output.
432  *
433  * Returns: The #GstCaps this track is configured to output.
434  */
435 const GstCaps *
436 ges_track_get_caps (GESTrack * track)
437 {
438   g_return_val_if_fail (GES_IS_TRACK (track), NULL);
439
440   return track->priv->caps;
441 }
442
443 /**
444  * ges_track_get_timeline:
445  * @track: a #GESTrack
446  *
447  * Get the #GESTimeline this track belongs to. Can be %NULL.
448  *
449  * Returns: The #GESTimeline this track belongs to. Can be %NULL.
450  */
451 const GESTimeline *
452 ges_track_get_timeline (GESTrack * track)
453 {
454   g_return_val_if_fail (GES_IS_TRACK (track), NULL);
455
456   return track->priv->timeline;
457 }