1 /* GStreamer Editing Services
2 * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
3 * 2009 Nokia Corporation
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.
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.
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.
23 * @short_description: Composition of objects
25 * Corresponds to one output format (i.e. audio OR video).
27 * Contains the compatible TrackObject(s).
29 * Wraps GNonLin's 'gnlcomposition' element.
32 #include "ges-internal.h"
33 #include "ges-track.h"
34 #include "ges-track-object.h"
36 G_DEFINE_TYPE (GESTrack, ges_track, GST_TYPE_BIN);
38 struct _GESTrackPrivate
41 GESTimeline *timeline;
47 GstElement *composition; /* The composition associated with this track */
48 GstPad *srcpad; /* The source GhostPad */
60 static GParamSpec *properties[ARG_LAST];
62 static void pad_added_cb (GstElement * element, GstPad * pad, GESTrack * track);
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);
69 ges_track_get_property (GObject * object, guint property_id,
70 GValue * value, GParamSpec * pspec)
72 GESTrack *track = GES_TRACK (object);
74 switch (property_id) {
76 gst_value_set_caps (value, track->priv->caps);
79 g_value_set_flags (value, track->type);
82 g_value_set_uint64 (value, track->priv->duration);
85 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
90 ges_track_set_property (GObject * object, guint property_id,
91 const GValue * value, GParamSpec * pspec)
93 GESTrack *track = GES_TRACK (object);
95 switch (property_id) {
97 ges_track_set_caps (track, gst_value_get_caps (value));
100 track->type = g_value_get_flags (value);
103 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
108 ges_track_dispose (GObject * object)
110 GESTrack *track = (GESTrack *) object;
111 GESTrackPrivate *priv = track->priv;
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);
120 if (priv->composition) {
121 gst_bin_remove (GST_BIN (object), priv->composition);
122 priv->composition = NULL;
126 gst_caps_unref (priv->caps);
130 G_OBJECT_CLASS (ges_track_parent_class)->dispose (object);
134 ges_track_finalize (GObject * object)
136 G_OBJECT_CLASS (ges_track_parent_class)->finalize (object);
140 ges_track_class_init (GESTrackClass * klass)
142 GObjectClass *object_class = G_OBJECT_CLASS (klass);
144 g_type_class_add_private (klass, sizeof (GESTrackPrivate));
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;
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.
157 * Default value: #GST_CAPS_ANY.
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]);
168 * Current duration of the track
172 properties[ARG_DURATION] = g_param_spec_uint64 ("duration", "Duration",
173 "The current duration of the track", 0, G_MAXUINT64, GST_SECOND,
175 g_object_class_install_property (object_class, ARG_DURATION,
176 properties[ARG_DURATION]);
179 * GESTrack:track-type
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.
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
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]);
197 ges_track_init (GESTrack * self)
199 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
200 GES_TYPE_TRACK, GESTrackPrivate);
202 self->priv->composition = gst_element_factory_make ("gnlcomposition", NULL);
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);
211 if (!gst_bin_add (GST_BIN (self), self->priv->composition))
212 GST_ERROR ("Couldn't add composition to bin !");
217 * @type: The type of track
218 * @caps: (transfer full): The caps to restrict the output of the track to.
220 * Creates a new #GESTrack with the given @type and @caps.
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.
225 * Returns: A new #GESTrack.
228 ges_track_new (GESTrackType type, GstCaps * caps)
232 track = g_object_new (GES_TYPE_TRACK, "caps", caps, "track-type", type, NULL);
233 gst_caps_unref (caps);
239 * ges_track_video_raw_new:
241 * Creates a new #GESTrack of type #GES_TRACK_TYPE_VIDEO and with generic
242 * raw video caps ("video/x-raw");
244 * Returns: A new #GESTrack.
247 ges_track_video_raw_new (void)
250 GstCaps *caps = gst_caps_new_empty_simple ("video/x-raw");
252 track = ges_track_new (GES_TRACK_TYPE_VIDEO, caps);
258 * ges_track_audio_raw_new:
260 * Creates a new #GESTrack of type #GES_TRACK_TYPE_AUDIO and with generic
261 * raw audio caps ("audio/x-raw");
263 * Returns: A new #GESTrack.
266 ges_track_audio_raw_new (void)
269 GstCaps *caps = gst_caps_new_empty_simple ("audio/x-raw");
271 track = ges_track_new (GES_TRACK_TYPE_AUDIO, caps);
277 * ges_track_set_timeline:
278 * @track: a #GESTrack
279 * @timeline: a #GESTimeline
281 * Sets @timeline as the timeline controlling @track.
284 ges_track_set_timeline (GESTrack * track, GESTimeline * timeline)
286 GST_DEBUG ("track:%p, timeline:%p", track, timeline);
288 track->priv->timeline = timeline;
292 * ges_track_set_caps:
293 * @track: a #GESTrack
294 * @caps: the #GstCaps to set
296 * Sets the given @caps on the track.
299 ges_track_set_caps (GESTrack * track, const GstCaps * caps)
301 GESTrackPrivate *priv;
303 g_return_if_fail (GES_IS_TRACK (track));
304 g_return_if_fail (GST_IS_CAPS (caps));
306 GST_DEBUG ("track:%p, caps:%" GST_PTR_FORMAT, track, caps);
311 gst_caps_unref (priv->caps);
312 priv->caps = gst_caps_copy (caps);
314 g_object_set (priv->composition, "caps", caps, NULL);
315 /* FIXME : update all trackobjects ? */
319 * ges_track_add_object:
320 * @track: a #GESTrack
321 * @object: (transfer full): the #GESTrackObject to add
323 * Adds the given object to the track. Sets the object's controlling track,
324 * and thus takes ownership of the @object.
326 * An object can only be added to one track.
328 * Returns: #TRUE if the object was properly added. #FALSE if the track does not
329 * want to accept the object.
332 ges_track_add_object (GESTrack * track, GESTrackObject * object)
334 g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
335 g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), FALSE);
337 GST_DEBUG ("track:%p, object:%p", track, object);
339 if (G_UNLIKELY (ges_track_object_get_track (object) != NULL)) {
340 GST_WARNING ("Object already belongs to another track");
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 !");
352 if (G_UNLIKELY (!ges_track_object_set_track (object, track))) {
353 GST_ERROR ("Couldn't properly add the object to the Track");
357 GST_DEBUG ("Adding object to ourself");
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");
365 g_object_ref_sink (object);
367 track->priv->trackobjects = g_list_append (track->priv->trackobjects, object);
373 * ges_track_remove_object:
374 * @track: a #GESTrack
375 * @object: the #GESTrackObject to remove
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.
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).
386 ges_track_remove_object (GESTrack * track, GESTrackObject * object)
388 GESTrackPrivate *priv;
389 GstElement *gnlobject;
391 g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
392 g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), FALSE);
394 GST_DEBUG ("track:%p, object:%p", track, object);
398 if (G_UNLIKELY (ges_track_object_get_track (object) != track)) {
399 GST_WARNING ("Object belongs to another track");
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");
412 ges_track_object_set_track (object, NULL);
413 priv->trackobjects = g_list_remove (priv->trackobjects, object);
415 g_object_unref (object);
421 pad_added_cb (GstElement * element, GstPad * pad, GESTrack * track)
423 GESTrackPrivate *priv = track->priv;
425 GST_DEBUG ("track:%p, pad %s:%s", track, GST_DEBUG_PAD_NAME (pad));
428 priv->srcpad = gst_ghost_pad_new ("src", pad);
430 gst_pad_set_active (priv->srcpad, TRUE);
432 gst_element_add_pad (GST_ELEMENT (track), priv->srcpad);
438 pad_removed_cb (GstElement * element, GstPad * pad, GESTrack * track)
440 GESTrackPrivate *priv = track->priv;
442 GST_DEBUG ("track:%p, pad %s:%s", track, GST_DEBUG_PAD_NAME (pad));
444 if (G_LIKELY (priv->srcpad)) {
445 gst_pad_set_active (priv->srcpad, FALSE);
446 gst_element_remove_pad (GST_ELEMENT (track), priv->srcpad);
454 composition_duration_cb (GstElement * composition,
455 GParamSpec * arg G_GNUC_UNUSED, GESTrack * obj)
459 g_object_get (composition, "duration", &duration, NULL);
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));
467 obj->priv->duration = duration;
469 #if GLIB_CHECK_VERSION(2,26,0)
470 g_object_notify_by_pspec (G_OBJECT (obj), properties[ARG_DURATION]);
472 g_object_notify (G_OBJECT (obj), "duration");
478 * ges_track_get_caps:
479 * @track: a #GESTrack
481 * Get the #GstCaps this track is configured to output.
483 * Returns: The #GstCaps this track is configured to output.
486 ges_track_get_caps (GESTrack * track)
488 g_return_val_if_fail (GES_IS_TRACK (track), NULL);
490 return track->priv->caps;
494 * ges_track_get_timeline:
495 * @track: a #GESTrack
497 * Get the #GESTimeline this track belongs to. Can be %NULL.
499 * Returns: The #GESTimeline this track belongs to. Can be %NULL.
502 ges_track_get_timeline (GESTrack * track)
504 g_return_val_if_fail (GES_IS_TRACK (track), NULL);
506 return track->priv->timeline;