GESTrack: The track steals the refcount to the caps. document that.
[platform/upstream/gstreamer.git] / ges / ges-track.c
1 /* GStreamer Editing Services
2  * Copyright (C) 2009 Edward Hervey <bilboed@bilboed.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /**
21  * SECTION:ges-track
22  * @short_description: Composition of objects
23  *
24  * Corresponds to one output format (i.e. audio OR video).
25  *
26  * Contains the compatible TrackObject(s).
27  *
28  * Wraps GNonLin's 'gnlcomposition' element.
29  */
30
31 #include "ges-internal.h"
32 #include "ges-track.h"
33 #include "ges-track-object.h"
34
35 G_DEFINE_TYPE (GESTrack, ges_track, GST_TYPE_BIN);
36
37 enum
38 {
39   ARG_0,
40   ARG_CAPS,
41   ARG_TYPE
42 };
43
44 static void pad_added_cb (GstElement * element, GstPad * pad, GESTrack * track);
45
46 static void
47 pad_removed_cb (GstElement * element, GstPad * pad, GESTrack * track);
48
49 #define C_ENUM(v) ((gint) v)
50 static void
51 register_ges_track_type_select_result (GType * id)
52 {
53   static const GEnumValue values[] = {
54     {C_ENUM (GES_TRACK_TYPE_AUDIO), "GES_TRACK_TYPE_AUDIO", "audio"},
55     {C_ENUM (GES_TRACK_TYPE_VIDEO), "GES_TRACK_TYPE_VIDEO", "video"},
56     {C_ENUM (GES_TRACK_TYPE_TEXT), "GES_TRACK_TYPE_TEXT", "text"},
57     {C_ENUM (GES_TRACK_TYPE_CUSTOM), "GES_TRACK_TYPE_CUSTOM", "custom"},
58     {0, NULL, NULL}
59   };
60
61   *id = g_enum_register_static ("GESTrackType", values);
62 }
63
64 GType
65 ges_track_type_get_type (void)
66 {
67   static GType id;
68   static GOnce once = G_ONCE_INIT;
69
70   g_once (&once, (GThreadFunc) register_ges_track_type_select_result, &id);
71   return id;
72 }
73
74 static void
75 ges_track_get_property (GObject * object, guint property_id,
76     GValue * value, GParamSpec * pspec)
77 {
78   GESTrack *track = GES_TRACK (object);
79
80   switch (property_id) {
81     case ARG_CAPS:
82       gst_value_set_caps (value, track->caps);
83       break;
84     case ARG_TYPE:
85       g_value_set_enum (value, track->type);
86       break;
87     default:
88       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
89   }
90 }
91
92 static void
93 ges_track_set_property (GObject * object, guint property_id,
94     const GValue * value, GParamSpec * pspec)
95 {
96   GESTrack *track = GES_TRACK (object);
97
98   switch (property_id) {
99     case ARG_CAPS:
100       ges_track_set_caps (track, gst_value_get_caps (value));
101       break;
102     case ARG_TYPE:
103       track->type = g_value_get_enum (value);
104       break;
105     default:
106       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
107   }
108 }
109
110 static void
111 ges_track_dispose (GObject * object)
112 {
113   GESTrack *track = (GESTrack *) object;
114
115   /* FIXME : Remove all TrackObjects ! */
116
117   if (track->composition) {
118     gst_bin_remove (GST_BIN (object), track->composition);
119     track->composition = NULL;
120   }
121
122   if (track->caps) {
123     gst_caps_unref (track->caps);
124     track->caps = NULL;
125   }
126
127   G_OBJECT_CLASS (ges_track_parent_class)->dispose (object);
128 }
129
130 static void
131 ges_track_finalize (GObject * object)
132 {
133   G_OBJECT_CLASS (ges_track_parent_class)->finalize (object);
134 }
135
136 static void
137 ges_track_class_init (GESTrackClass * klass)
138 {
139   GObjectClass *object_class = G_OBJECT_CLASS (klass);
140
141   object_class->get_property = ges_track_get_property;
142   object_class->set_property = ges_track_set_property;
143   object_class->dispose = ges_track_dispose;
144   object_class->finalize = ges_track_finalize;
145
146   /**
147    * GESTrack:caps
148    *
149    * Caps used to filter/choose the output stream. This is generally set to
150    * a generic set of caps like 'video/x-raw-rgb;video/x-raw-yuv' for raw video.
151    *
152    * Default value: #GST_CAPS_ANY.
153    */
154   g_object_class_install_property (object_class, ARG_CAPS,
155       g_param_spec_boxed ("caps", "Caps",
156           "Caps used to filter/choose the output stream",
157           GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
158
159   /**
160    * GESTrack:track-type
161    *
162    * Type of stream the track outputs. This is used when creating the #GESTrack
163    * to specify in generic terms what type of content will be outputted.
164    *
165    * It also serves as a 'fast' way to check what type of data will be outputted
166    * from the #GESTrack without having to actually check the #GESTrack's caps
167    * property.
168    */
169   g_object_class_install_property (object_class, ARG_TYPE,
170       g_param_spec_enum ("track-type", "TrackType",
171           "Type of stream the track outputs",
172           GES_TYPE_TRACK_TYPE, GES_TRACK_TYPE_CUSTOM,
173           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
174 }
175
176 static void
177 ges_track_init (GESTrack * self)
178 {
179   self->composition = gst_element_factory_make ("gnlcomposition", NULL);
180
181   g_signal_connect (self->composition, "pad-added", (GCallback) pad_added_cb,
182       self);
183   g_signal_connect (self->composition, "pad-removed",
184       (GCallback) pad_removed_cb, self);
185
186   if (!gst_bin_add (GST_BIN (self), self->composition))
187     GST_ERROR ("Couldn't add composition to bin !");
188 }
189
190 /**
191  * ges_track_new:
192  * @type: The type of track
193  * @caps: The caps to restrict the output of the track to.
194  *
195  * Creates a new #GESTrack with the given @type and @caps.
196  *
197  * The newly created track will steal a reference to the caps. If you wish to 
198  * use those caps elsewhere, you will have to take an extra reference.
199  *
200  * Returns: A new #GESTrack.
201  */
202 GESTrack *
203 ges_track_new (GESTrackType type, GstCaps * caps)
204 {
205   GESTrack *track;
206
207   track = g_object_new (GES_TYPE_TRACK, "caps", caps, "track-type", type, NULL);
208   gst_caps_unref (caps);
209
210   return track;
211 }
212
213 /**
214  * ges_track_video_raw_new:
215  *
216  * Creates a new #GESTrack of type #GES_TRACK_TYPE_VIDEO and with generic
217  * raw video caps ("video/x-raw-yuv;video/x-raw-rgb");
218  *
219  * Returns: A new #GESTrack.
220  */
221 GESTrack *
222 ges_track_video_raw_new ()
223 {
224   GESTrack *track;
225   GstCaps *caps = gst_caps_from_string ("video/x-raw-yuv;video/x-raw-rgb");
226
227   track = ges_track_new (GES_TRACK_TYPE_VIDEO, caps);
228
229   return track;
230 }
231
232 /**
233  * ges_track_audio_raw_new:
234  *
235  * Creates a new #GESTrack of type #GES_TRACK_TYPE_AUDIO and with generic
236  * raw audio caps ("audio/x-raw-int;audio/x-raw-float");
237  *
238  * Returns: A new #GESTrack.
239  */
240 GESTrack *
241 ges_track_audio_raw_new ()
242 {
243   GESTrack *track;
244   GstCaps *caps = gst_caps_from_string ("audio/x-raw-int;audio/x-raw-float");
245
246   track = ges_track_new (GES_TRACK_TYPE_AUDIO, caps);
247
248   return track;
249 }
250
251 void
252 ges_track_set_timeline (GESTrack * track, GESTimeline * timeline)
253 {
254   GST_DEBUG ("track:%p, timeline:%p", track, timeline);
255
256   track->timeline = timeline;
257 }
258
259 /**
260  * ges_track_set_caps:
261  * @track: a #GESTrack
262  * @caps: the #GstCaps to set
263  *
264  * Sets the given @caps on the track.
265  */
266 void
267 ges_track_set_caps (GESTrack * track, const GstCaps * caps)
268 {
269   GST_DEBUG ("track:%p, caps:%" GST_PTR_FORMAT, track, caps);
270
271   g_return_if_fail (GST_IS_CAPS (caps));
272
273   if (track->caps)
274     gst_caps_unref (track->caps);
275   track->caps = gst_caps_copy (caps);
276
277   /* FIXME : update all trackobjects ? */
278 }
279
280 /**
281  * ges_track_add_object:
282  * @track: a #GESTrack
283  * @object: the #GESTrackObject to add
284  *
285  * Adds the given object to the track.
286  *
287  * Returns: #TRUE if the object was properly added. #FALSE if the track does not
288  * want to accept the object.
289  */
290 gboolean
291 ges_track_add_object (GESTrack * track, GESTrackObject * object)
292 {
293   GST_DEBUG ("track:%p, object:%p", track, object);
294
295   if (G_UNLIKELY (object->track != NULL)) {
296     GST_WARNING ("Object already belongs to another track");
297     return FALSE;
298   }
299
300   if (G_UNLIKELY (object->gnlobject != NULL)) {
301     GST_ERROR ("TrackObject doesn't have a gnlobject !");
302     return FALSE;
303   }
304
305   if (G_UNLIKELY (!ges_track_object_set_track (object, track))) {
306     GST_ERROR ("Couldn't properly add the object to the Track");
307     return FALSE;
308   }
309
310   GST_DEBUG ("Adding object to ourself");
311
312   /* make sure the object has a valid gnlobject ! */
313   if (G_UNLIKELY (!gst_bin_add (GST_BIN (track->composition),
314               object->gnlobject))) {
315     GST_WARNING ("Couldn't add object to the GnlComposition");
316     return FALSE;
317   }
318
319   return TRUE;
320 }
321
322 /**
323  * ges_track_remove_object:
324  * @track: a #GESTrack
325  * @object: the #GESTrackObject to remove
326  *
327  * Removes the object from the track.
328  *
329  * Returns: #TRUE if the object was removed, else #FALSE if the track
330  * could not remove the object (like if it didn't belong to the track).
331  */
332 gboolean
333 ges_track_remove_object (GESTrack * track, GESTrackObject * object)
334 {
335   GST_DEBUG ("track:%p, object:%p", track, object);
336
337   if (G_UNLIKELY (object->track != track)) {
338     GST_WARNING ("Object belongs to another track");
339     return FALSE;
340   }
341
342   if (G_LIKELY (object->gnlobject != NULL)) {
343     GST_DEBUG ("Removing GnlObject from composition");
344     if (!gst_bin_remove (GST_BIN (track->composition), object->gnlobject)) {
345       GST_WARNING ("Failed to remove gnlobject from composition");
346       return FALSE;
347     }
348   }
349
350   ges_track_object_set_track (object, NULL);
351
352   return TRUE;
353 }
354
355 static void
356 pad_added_cb (GstElement * element, GstPad * pad, GESTrack * track)
357 {
358   GST_DEBUG ("track:%p, pad %s:%s", track, GST_DEBUG_PAD_NAME (pad));
359
360   /* ghost the pad */
361   track->srcpad = gst_ghost_pad_new ("src", pad);
362
363   gst_pad_set_active (track->srcpad, TRUE);
364
365   gst_element_add_pad (GST_ELEMENT (track), track->srcpad);
366
367   GST_DEBUG ("done");
368 }
369
370 static void
371 pad_removed_cb (GstElement * element, GstPad * pad, GESTrack * track)
372 {
373   GST_DEBUG ("track:%p, pad %s:%s", track, GST_DEBUG_PAD_NAME (pad));
374
375   if (G_LIKELY (track->srcpad)) {
376     gst_pad_set_active (track->srcpad, FALSE);
377     gst_element_remove_pad (GST_ELEMENT (track), track->srcpad);
378     track->srcpad = NULL;
379   }
380
381   GST_DEBUG ("done");
382 }