ges: port to new raw audio/video caps
[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   guint64 duration;
44
45   GstCaps *caps;
46
47   GstElement *composition;      /* The composition associated with this track */
48   GstPad *srcpad;               /* The source GhostPad */
49 };
50
51 enum
52 {
53   ARG_0,
54   ARG_CAPS,
55   ARG_TYPE,
56   ARG_DURATION,
57   ARG_LAST
58 };
59
60 static GParamSpec *properties[ARG_LAST];
61
62 static void pad_added_cb (GstElement * element, GstPad * pad, GESTrack * track);
63 static void
64 pad_removed_cb (GstElement * element, GstPad * pad, GESTrack * track);
65 static void composition_duration_cb (GstElement * composition, GParamSpec * arg
66     G_GNUC_UNUSED, GESTrack * obj);
67
68 static void
69 ges_track_get_property (GObject * object, guint property_id,
70     GValue * value, GParamSpec * pspec)
71 {
72   GESTrack *track = GES_TRACK (object);
73
74   switch (property_id) {
75     case ARG_CAPS:
76       gst_value_set_caps (value, track->priv->caps);
77       break;
78     case ARG_TYPE:
79       g_value_set_flags (value, track->type);
80       break;
81     case ARG_DURATION:
82       g_value_set_uint64 (value, track->priv->duration);
83       break;
84     default:
85       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
86   }
87 }
88
89 static void
90 ges_track_set_property (GObject * object, guint property_id,
91     const GValue * value, GParamSpec * pspec)
92 {
93   GESTrack *track = GES_TRACK (object);
94
95   switch (property_id) {
96     case ARG_CAPS:
97       ges_track_set_caps (track, gst_value_get_caps (value));
98       break;
99     case ARG_TYPE:
100       track->type = g_value_get_flags (value);
101       break;
102     default:
103       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
104   }
105 }
106
107 static void
108 ges_track_dispose (GObject * object)
109 {
110   GESTrack *track = (GESTrack *) object;
111   GESTrackPrivate *priv = track->priv;
112
113   while (priv->trackobjects) {
114     GESTrackObject *trobj = GES_TRACK_OBJECT (priv->trackobjects->data);
115     ges_track_remove_object (track, trobj);
116     ges_timeline_object_release_track_object ((GESTimelineObject *)
117         ges_track_object_get_timeline_object (trobj), trobj);
118   }
119
120   if (priv->composition) {
121     gst_bin_remove (GST_BIN (object), priv->composition);
122     priv->composition = NULL;
123   }
124
125   if (priv->caps) {
126     gst_caps_unref (priv->caps);
127     priv->caps = NULL;
128   }
129
130   G_OBJECT_CLASS (ges_track_parent_class)->dispose (object);
131 }
132
133 static void
134 ges_track_finalize (GObject * object)
135 {
136   G_OBJECT_CLASS (ges_track_parent_class)->finalize (object);
137 }
138
139 static void
140 ges_track_class_init (GESTrackClass * klass)
141 {
142   GObjectClass *object_class = G_OBJECT_CLASS (klass);
143
144   g_type_class_add_private (klass, sizeof (GESTrackPrivate));
145
146   object_class->get_property = ges_track_get_property;
147   object_class->set_property = ges_track_set_property;
148   object_class->dispose = ges_track_dispose;
149   object_class->finalize = ges_track_finalize;
150
151   /**
152    * GESTrack:caps
153    *
154    * Caps used to filter/choose the output stream. This is generally set to
155    * a generic set of caps like 'video/x-raw' for raw video.
156    *
157    * Default value: #GST_CAPS_ANY.
158    */
159   properties[ARG_CAPS] = g_param_spec_boxed ("caps", "Caps",
160       "Caps used to filter/choose the output stream",
161       GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
162   g_object_class_install_property (object_class, ARG_CAPS,
163       properties[ARG_CAPS]);
164
165   /**
166    * GESTrack:duration
167    *
168    * Current duration of the track
169    *
170    * Default value: O
171    */
172   properties[ARG_DURATION] = g_param_spec_uint64 ("duration", "Duration",
173       "The current duration of the track", 0, G_MAXUINT64, GST_SECOND,
174       G_PARAM_READABLE);
175   g_object_class_install_property (object_class, ARG_DURATION,
176       properties[ARG_DURATION]);
177
178   /**
179    * GESTrack:track-type
180    *
181    * Type of stream the track outputs. This is used when creating the #GESTrack
182    * to specify in generic terms what type of content will be outputted.
183    *
184    * It also serves as a 'fast' way to check what type of data will be outputted
185    * from the #GESTrack without having to actually check the #GESTrack's caps
186    * property.
187    */
188   properties[ARG_TYPE] = g_param_spec_flags ("track-type", "TrackType",
189       "Type of stream the track outputs",
190       GES_TYPE_TRACK_TYPE, GES_TRACK_TYPE_CUSTOM,
191       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
192   g_object_class_install_property (object_class, ARG_TYPE,
193       properties[ARG_TYPE]);
194 }
195
196 static void
197 ges_track_init (GESTrack * self)
198 {
199   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
200       GES_TYPE_TRACK, GESTrackPrivate);
201
202   self->priv->composition = gst_element_factory_make ("gnlcomposition", NULL);
203
204   g_signal_connect (G_OBJECT (self->priv->composition), "notify::duration",
205       G_CALLBACK (composition_duration_cb), self);
206   g_signal_connect (self->priv->composition, "pad-added",
207       (GCallback) pad_added_cb, self);
208   g_signal_connect (self->priv->composition, "pad-removed",
209       (GCallback) pad_removed_cb, self);
210
211   if (!gst_bin_add (GST_BIN (self), self->priv->composition))
212     GST_ERROR ("Couldn't add composition to bin !");
213 }
214
215 /**
216  * ges_track_new:
217  * @type: The type of track
218  * @caps: The caps to restrict the output of the track to.
219  *
220  * Creates a new #GESTrack with the given @type and @caps.
221  *
222  * The newly created track will steal a reference to the caps. If you wish to 
223  * use those caps elsewhere, you will have to take an extra reference.
224  *
225  * Returns: A new #GESTrack.
226  */
227 GESTrack *
228 ges_track_new (GESTrackType type, GstCaps * caps)
229 {
230   GESTrack *track;
231
232   track = g_object_new (GES_TYPE_TRACK, "caps", caps, "track-type", type, NULL);
233   gst_caps_unref (caps);
234
235   return track;
236 }
237
238 /**
239  * ges_track_video_raw_new:
240  *
241  * Creates a new #GESTrack of type #GES_TRACK_TYPE_VIDEO and with generic
242  * raw video caps ("video/x-raw");
243  *
244  * Returns: A new #GESTrack.
245  */
246 GESTrack *
247 ges_track_video_raw_new (void)
248 {
249   GESTrack *track;
250   GstCaps *caps = gst_caps_new_empty_simple ("video/x-raw");
251
252   track = ges_track_new (GES_TRACK_TYPE_VIDEO, caps);
253
254   return track;
255 }
256
257 /**
258  * ges_track_audio_raw_new:
259  *
260  * Creates a new #GESTrack of type #GES_TRACK_TYPE_AUDIO and with generic
261  * raw audio caps ("audio/x-raw");
262  *
263  * Returns: A new #GESTrack.
264  */
265 GESTrack *
266 ges_track_audio_raw_new (void)
267 {
268   GESTrack *track;
269   GstCaps *caps = gst_caps_new_empty_simple ("audio/x-raw");
270
271   track = ges_track_new (GES_TRACK_TYPE_AUDIO, caps);
272
273   return track;
274 }
275
276 /**
277  * ges_track_set_timeline:
278  * @track: a #GESTrack
279  * @timeline: a #GESTimeline
280  *
281  * Sets @timeline as the timeline controlling @track.
282  */
283 void
284 ges_track_set_timeline (GESTrack * track, GESTimeline * timeline)
285 {
286   GST_DEBUG ("track:%p, timeline:%p", track, timeline);
287
288   track->priv->timeline = timeline;
289 }
290
291 /**
292  * ges_track_set_caps:
293  * @track: a #GESTrack
294  * @caps: the #GstCaps to set
295  *
296  * Sets the given @caps on the track.
297  */
298 void
299 ges_track_set_caps (GESTrack * track, const GstCaps * caps)
300 {
301   GESTrackPrivate *priv;
302
303   g_return_if_fail (GES_IS_TRACK (track));
304   g_return_if_fail (GST_IS_CAPS (caps));
305
306   GST_DEBUG ("track:%p, caps:%" GST_PTR_FORMAT, track, caps);
307
308   priv = track->priv;
309
310   if (priv->caps)
311     gst_caps_unref (priv->caps);
312   priv->caps = gst_caps_copy (caps);
313
314   g_object_set (priv->composition, "caps", caps, NULL);
315   /* FIXME : update all trackobjects ? */
316 }
317
318 /**
319  * ges_track_add_object:
320  * @track: a #GESTrack
321  * @object: (transfer full): the #GESTrackObject to add
322  *
323  * Adds the given object to the track. Sets the object's controlling track,
324  * and thus takes ownership of the @object.
325  *
326  * An object can only be added to one track.
327  *
328  * Returns: #TRUE if the object was properly added. #FALSE if the track does not
329  * want to accept the object.
330  */
331 gboolean
332 ges_track_add_object (GESTrack * track, GESTrackObject * object)
333 {
334   g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
335   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), FALSE);
336
337   GST_DEBUG ("track:%p, object:%p", track, object);
338
339   if (G_UNLIKELY (ges_track_object_get_track (object) != NULL)) {
340     GST_WARNING ("Object already belongs to another track");
341     return FALSE;
342   }
343
344   /* At this point, the track object shouldn't have any gnlobject since
345    * it hasn't been added to a track yet.
346    * FIXME : This check seems a bit obsolete */
347   if (G_UNLIKELY (ges_track_object_get_gnlobject (object) != NULL)) {
348     GST_ERROR ("TrackObject already controls a gnlobject !");
349     return FALSE;
350   }
351
352   if (G_UNLIKELY (!ges_track_object_set_track (object, track))) {
353     GST_ERROR ("Couldn't properly add the object to the Track");
354     return FALSE;
355   }
356
357   GST_DEBUG ("Adding object to ourself");
358
359   if (G_UNLIKELY (!gst_bin_add (GST_BIN (track->priv->composition),
360               ges_track_object_get_gnlobject (object)))) {
361     GST_WARNING ("Couldn't add object to the GnlComposition");
362     return FALSE;
363   }
364
365   g_object_ref_sink (object);
366
367   track->priv->trackobjects = g_list_append (track->priv->trackobjects, object);
368
369   return TRUE;
370 }
371
372 /**
373  * ges_track_remove_object:
374  * @track: a #GESTrack
375  * @object: the #GESTrackObject to remove
376  *
377  * Removes the object from the track and unparents it.
378  * Unparenting it means the reference owned by @track on the @object will be
379  * removed. If you wish to use the @object after this function, make sure you
380  * call g_object_ref() before removing it from the @track.
381  *
382  * Returns: #TRUE if the object was removed, else #FALSE if the track
383  * could not remove the object (like if it didn't belong to the track).
384  */
385 gboolean
386 ges_track_remove_object (GESTrack * track, GESTrackObject * object)
387 {
388   GESTrackPrivate *priv;
389   GstElement *gnlobject;
390
391   g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
392   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), FALSE);
393
394   GST_DEBUG ("track:%p, object:%p", track, object);
395
396   priv = track->priv;
397
398   if (G_UNLIKELY (ges_track_object_get_track (object) != track)) {
399     GST_WARNING ("Object belongs to another track");
400     return FALSE;
401   }
402
403   if ((gnlobject = ges_track_object_get_gnlobject (object))) {
404     GST_DEBUG ("Removing GnlObject '%s' from composition '%s'",
405         GST_ELEMENT_NAME (gnlobject), GST_ELEMENT_NAME (priv->composition));
406     if (!gst_bin_remove (GST_BIN (priv->composition), gnlobject)) {
407       GST_WARNING ("Failed to remove gnlobject from composition");
408       return FALSE;
409     }
410   }
411
412   ges_track_object_set_track (object, NULL);
413   priv->trackobjects = g_list_remove (priv->trackobjects, object);
414
415   g_object_unref (object);
416
417   return TRUE;
418 }
419
420 static void
421 pad_added_cb (GstElement * element, GstPad * pad, GESTrack * track)
422 {
423   GESTrackPrivate *priv = track->priv;
424
425   GST_DEBUG ("track:%p, pad %s:%s", track, GST_DEBUG_PAD_NAME (pad));
426
427   /* ghost the pad */
428   priv->srcpad = gst_ghost_pad_new ("src", pad);
429
430   gst_pad_set_active (priv->srcpad, TRUE);
431
432   gst_element_add_pad (GST_ELEMENT (track), priv->srcpad);
433
434   GST_DEBUG ("done");
435 }
436
437 static void
438 pad_removed_cb (GstElement * element, GstPad * pad, GESTrack * track)
439 {
440   GESTrackPrivate *priv = track->priv;
441
442   GST_DEBUG ("track:%p, pad %s:%s", track, GST_DEBUG_PAD_NAME (pad));
443
444   if (G_LIKELY (priv->srcpad)) {
445     gst_pad_set_active (priv->srcpad, FALSE);
446     gst_element_remove_pad (GST_ELEMENT (track), priv->srcpad);
447     priv->srcpad = NULL;
448   }
449
450   GST_DEBUG ("done");
451 }
452
453 static void
454 composition_duration_cb (GstElement * composition,
455     GParamSpec * arg G_GNUC_UNUSED, GESTrack * obj)
456 {
457   guint64 duration;
458
459   g_object_get (composition, "duration", &duration, NULL);
460
461
462   if (obj->priv->duration != duration) {
463     GST_DEBUG ("composition duration : %" GST_TIME_FORMAT " current : %"
464         GST_TIME_FORMAT, GST_TIME_ARGS (duration),
465         GST_TIME_ARGS (obj->priv->duration));
466
467     obj->priv->duration = duration;
468
469 #if GLIB_CHECK_VERSION(2,26,0)
470     g_object_notify_by_pspec (G_OBJECT (obj), properties[ARG_DURATION]);
471 #else
472     g_object_notify (G_OBJECT (obj), "duration");
473 #endif
474   }
475 }
476
477 /**
478  * ges_track_get_caps:
479  * @track: a #GESTrack
480  *
481  * Get the #GstCaps this track is configured to output.
482  *
483  * Returns: The #GstCaps this track is configured to output.
484  */
485 const GstCaps *
486 ges_track_get_caps (GESTrack * track)
487 {
488   g_return_val_if_fail (GES_IS_TRACK (track), NULL);
489
490   return track->priv->caps;
491 }
492
493 /**
494  * ges_track_get_timeline:
495  * @track: a #GESTrack
496  *
497  * Get the #GESTimeline this track belongs to. Can be %NULL.
498  *
499  * Returns: The #GESTimeline this track belongs to. Can be %NULL.
500  */
501 const GESTimeline *
502 ges_track_get_timeline (GESTrack * track)
503 {
504   g_return_val_if_fail (GES_IS_TRACK (track), NULL);
505
506   return track->priv->timeline;
507 }