timeline-object: Add TrackObject to the Track after the TimelineObject
[platform/upstream/gstreamer.git] / ges / ges-track-video-transition.c
1 /* GStreamer Editing Services
2  * Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
3  *               2010 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-video-transition
23  * @short_description: implements video crossfade transition
24  */
25
26 #include <ges/ges.h>
27 #include "ges-internal.h"
28
29 G_DEFINE_TYPE (GESTrackVideoTransition, ges_track_video_transition,
30     GES_TYPE_TRACK_TRANSITION);
31
32 struct _GESTrackVideoTransitionPrivate
33 {
34   GESVideoStandardTransitionType type;
35
36   /* these enable video interpolation */
37   GstController *controller;
38   GstInterpolationControlSource *control_source;
39
40   /* so we can support changing between wipes */
41   GstElement *smpte;
42   GstElement *mixer;
43   GstPad *sinka;
44   GstPad *sinkb;
45
46   /* these will be different depending on whether smptealpha or alpha element
47    * is used */
48   gdouble start_value;
49   gdouble end_value;
50 };
51
52 enum
53 {
54   PROP_0,
55 };
56
57 #define fast_element_link(a,b) gst_element_link_pads_full((a),"src",(b),"sink",GST_PAD_LINK_CHECK_NOTHING)
58
59 static GObject *link_element_to_mixer (GstElement * element,
60     GstElement * mixer);
61
62 static GObject *link_element_to_mixer_with_smpte (GstBin * bin,
63     GstElement * element, GstElement * mixer, gint type,
64     GstElement ** smpteref);
65
66 static void
67 ges_track_video_transition_duration_changed (GESTrackObject * self,
68     guint64 duration);
69
70 static GstElement *ges_track_video_transition_create_element (GESTrackObject
71     * self);
72
73 static void ges_track_video_transition_dispose (GObject * object);
74
75 static void ges_track_video_transition_finalize (GObject * object);
76
77 static void ges_track_video_transition_get_property (GObject * object, guint
78     property_id, GValue * value, GParamSpec * pspec);
79
80 static void ges_track_video_transition_set_property (GObject * object, guint
81     property_id, const GValue * value, GParamSpec * pspec);
82
83 static void
84 ges_track_video_transition_class_init (GESTrackVideoTransitionClass * klass)
85 {
86   GObjectClass *object_class;
87   GESTrackObjectClass *toclass;
88
89   g_type_class_add_private (klass, sizeof (GESTrackVideoTransitionPrivate));
90
91   object_class = G_OBJECT_CLASS (klass);
92   toclass = GES_TRACK_OBJECT_CLASS (klass);
93
94   object_class->get_property = ges_track_video_transition_get_property;
95   object_class->set_property = ges_track_video_transition_set_property;
96   object_class->dispose = ges_track_video_transition_dispose;
97   object_class->finalize = ges_track_video_transition_finalize;
98
99   toclass->duration_changed = ges_track_video_transition_duration_changed;
100   toclass->create_element = ges_track_video_transition_create_element;
101 }
102
103 static void
104 ges_track_video_transition_init (GESTrackVideoTransition * self)
105 {
106   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
107       GES_TYPE_TRACK_VIDEO_TRANSITION, GESTrackVideoTransitionPrivate);
108
109   self->priv->controller = NULL;
110   self->priv->control_source = NULL;
111   self->priv->smpte = NULL;
112   self->priv->mixer = NULL;
113   self->priv->sinka = NULL;
114   self->priv->sinkb = NULL;
115   self->priv->type = GES_VIDEO_STANDARD_TRANSITION_TYPE_NONE;
116   self->priv->start_value = 0.0;
117   self->priv->end_value = 0.0;
118 }
119
120 static void
121 ges_track_video_transition_dispose (GObject * object)
122 {
123   GESTrackVideoTransition *self = GES_TRACK_VIDEO_TRANSITION (object);
124   GESTrackVideoTransitionPrivate *priv = self->priv;
125
126   GST_DEBUG ("disposing");
127   GST_LOG ("mixer: %p smpte: %p sinka: %p sinkb: %p",
128       priv->mixer, priv->smpte, priv->sinka, priv->sinkb);
129
130   if (priv->controller) {
131     g_object_unref (priv->controller);
132     priv->controller = NULL;
133     if (priv->control_source)
134       gst_object_unref (priv->control_source);
135     priv->control_source = NULL;
136   }
137
138   if (priv->sinka && priv->sinkb) {
139     GST_DEBUG ("releasing request pads for mixer");
140     gst_element_release_request_pad (priv->mixer, priv->sinka);
141     gst_element_release_request_pad (priv->mixer, priv->sinkb);
142     gst_object_unref (priv->sinka);
143     gst_object_unref (priv->sinkb);
144     priv->sinka = NULL;
145     priv->sinkb = NULL;
146   }
147
148   if (priv->mixer) {
149     GST_LOG ("unrefing mixer");
150     gst_object_unref (priv->mixer);
151     priv->mixer = NULL;
152   }
153
154   G_OBJECT_CLASS (ges_track_video_transition_parent_class)->dispose (object);
155 }
156
157 static void
158 ges_track_video_transition_finalize (GObject * object)
159 {
160   G_OBJECT_CLASS (ges_track_video_transition_parent_class)->finalize (object);
161 }
162
163 static void
164 ges_track_video_transition_get_property (GObject * object,
165     guint property_id, GValue * value, GParamSpec * pspec)
166 {
167   switch (property_id) {
168     default:
169       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
170   }
171 }
172
173 static void
174 ges_track_video_transition_set_property (GObject * object,
175     guint property_id, const GValue * value, GParamSpec * pspec)
176 {
177   switch (property_id) {
178     default:
179       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
180   }
181 }
182
183 static void
184 on_caps_set (GstPad * srca_pad, GParamSpec * pspec, GstElement * capsfilt)
185 {
186   gint width, height;
187   const GstStructure *str;
188   GstCaps *size_caps = NULL;
189
190   if (GST_PAD_CAPS (srca_pad)) {
191     /* Get width and height of first video */
192     str = gst_caps_get_structure (GST_PAD_CAPS (srca_pad), 0);
193     gst_structure_get_int (str, "width", &width);
194     gst_structure_get_int (str, "height", &height);
195
196     /* Set capsfilter to the size of the first video */
197     size_caps =
198         gst_caps_new_simple ("video/x-raw-yuv", "width", G_TYPE_INT, width,
199         "height", G_TYPE_INT, height, NULL);
200     g_object_set (capsfilt, "caps", size_caps, NULL);
201   }
202 }
203
204 static GstElement *
205 ges_track_video_transition_create_element (GESTrackObject * object)
206 {
207   GstElement *topbin, *iconva, *iconvb, *scalea, *scaleb, *capsfilt, *oconv;
208   GObject *target = NULL;
209   const gchar *propname = NULL;
210   GstElement *mixer = NULL;
211   GstPad *sinka_target, *sinkb_target, *src_target, *sinka, *sinkb, *src,
212       *srca_pad;
213   GstController *controller;
214   GstInterpolationControlSource *control_source;
215   GESTrackVideoTransition *self;
216   GESTrackVideoTransitionPrivate *priv;
217
218   self = GES_TRACK_VIDEO_TRANSITION (object);
219   priv = self->priv;
220
221   GST_LOG ("creating a video bin");
222
223   topbin = gst_bin_new ("transition-bin");
224   iconva = gst_element_factory_make ("ffmpegcolorspace", "tr-csp-a");
225   iconvb = gst_element_factory_make ("ffmpegcolorspace", "tr-csp-b");
226   scalea = gst_element_factory_make ("videoscale", "vs-a");
227   scaleb = gst_element_factory_make ("videoscale", "vs-b");
228   capsfilt = gst_element_factory_make ("capsfilter", "capsfilt");
229   oconv = gst_element_factory_make ("ffmpegcolorspace", "tr-csp-output");
230
231   gst_bin_add_many (GST_BIN (topbin), iconva, iconvb, scalea, scaleb, capsfilt,
232       oconv, NULL);
233
234   /* Prefer videomixer2 to videomixer */
235   mixer = gst_element_factory_make ("videomixer2", NULL);
236   if (mixer == NULL)
237     mixer = gst_element_factory_make ("videomixer", NULL);
238   g_object_set (G_OBJECT (mixer), "background", 1, NULL);
239   gst_bin_add (GST_BIN (topbin), mixer);
240
241   if (priv->type != GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE) {
242     priv->sinka =
243         (GstPad *) link_element_to_mixer_with_smpte (GST_BIN (topbin), iconva,
244         mixer, priv->type, NULL);
245     priv->sinkb =
246         (GstPad *) link_element_to_mixer_with_smpte (GST_BIN (topbin), iconvb,
247         mixer, priv->type, &priv->smpte);
248     target = (GObject *) priv->smpte;
249     propname = "position";
250     priv->start_value = 1.0;
251     priv->end_value = 0.0;
252   } else {
253     gst_element_link_pads_full (iconva, "src", scalea, "sink",
254         GST_PAD_LINK_CHECK_NOTHING);
255     gst_element_link_pads_full (iconvb, "src", scaleb, "sink",
256         GST_PAD_LINK_CHECK_NOTHING);
257     gst_element_link_pads_full (scaleb, "src", capsfilt, "sink",
258         GST_PAD_LINK_CHECK_NOTHING);
259
260     priv->sinka = (GstPad *) link_element_to_mixer (scalea, mixer);
261     priv->sinkb = (GstPad *) link_element_to_mixer (capsfilt, mixer);
262     target = (GObject *) priv->sinkb;
263     propname = "alpha";
264     priv->start_value = 0.0;
265     priv->end_value = 1.0;
266   }
267
268   priv->mixer = gst_object_ref (mixer);
269
270   fast_element_link (mixer, oconv);
271
272   sinka_target = gst_element_get_static_pad (iconva, "sink");
273   sinkb_target = gst_element_get_static_pad (iconvb, "sink");
274   src_target = gst_element_get_static_pad (oconv, "src");
275
276   sinka = gst_ghost_pad_new ("sinka", sinka_target);
277   sinkb = gst_ghost_pad_new ("sinkb", sinkb_target);
278   src = gst_ghost_pad_new ("src", src_target);
279
280   gst_element_add_pad (topbin, src);
281   gst_element_add_pad (topbin, sinka);
282   gst_element_add_pad (topbin, sinkb);
283
284   srca_pad = gst_element_get_static_pad (scalea, "src");
285   g_signal_connect (srca_pad, "notify::caps", G_CALLBACK (on_caps_set),
286       (GstElement *) capsfilt);
287
288   gst_object_unref (sinka_target);
289   gst_object_unref (sinkb_target);
290   gst_object_unref (src_target);
291   gst_object_unref (srca_pad);
292
293   /* set up interpolation */
294
295   g_object_set (target, propname, (gfloat) 0.0, NULL);
296
297   controller = gst_object_control_properties (target, propname, NULL);
298
299   control_source = gst_interpolation_control_source_new ();
300   gst_controller_set_control_source (controller,
301       propname, GST_CONTROL_SOURCE (control_source));
302   gst_interpolation_control_source_set_interpolation_mode (control_source,
303       GST_INTERPOLATE_LINEAR);
304
305   priv->controller = controller;
306   priv->control_source = control_source;
307
308   return topbin;
309 }
310
311 static GObject *
312 link_element_to_mixer (GstElement * element, GstElement * mixer)
313 {
314   GstPad *sinkpad = gst_element_get_request_pad (mixer, "sink_%d");
315   GstPad *srcpad = gst_element_get_static_pad (element, "src");
316
317   gst_pad_link_full (srcpad, sinkpad, GST_PAD_LINK_CHECK_NOTHING);
318   gst_object_unref (srcpad);
319
320   return G_OBJECT (sinkpad);
321 }
322
323 static GObject *
324 link_element_to_mixer_with_smpte (GstBin * bin, GstElement * element,
325     GstElement * mixer, gint type, GstElement ** smpteref)
326 {
327   GstPad *srcpad, *sinkpad;
328   GstElement *smptealpha = gst_element_factory_make ("smptealpha", NULL);
329
330   g_object_set (G_OBJECT (smptealpha),
331       "type", (gint) type, "invert", (gboolean) TRUE, NULL);
332   gst_bin_add (bin, smptealpha);
333
334   fast_element_link (element, smptealpha);
335
336   /* crack */
337   if (smpteref) {
338     *smpteref = smptealpha;
339   }
340
341   srcpad = gst_element_get_static_pad (smptealpha, "src");
342   sinkpad = gst_element_get_request_pad (mixer, "sink_%d");
343   gst_pad_link_full (srcpad, sinkpad, GST_PAD_LINK_CHECK_NOTHING);
344   gst_object_unref (srcpad);
345
346   return G_OBJECT (sinkpad);
347 }
348
349 static void
350 ges_track_video_transition_duration_changed (GESTrackObject * object,
351     guint64 duration)
352 {
353   GValue start_value = { 0, };
354   GValue end_value = { 0, };
355   GstElement *gnlobj = ges_track_object_get_gnlobject (object);
356   GESTrackVideoTransition *self = GES_TRACK_VIDEO_TRANSITION (object);
357   GESTrackVideoTransitionPrivate *priv = self->priv;
358
359   GST_LOG ("updating controller");
360
361   if (G_UNLIKELY (!gnlobj || !priv->control_source))
362     return;
363
364   GST_INFO ("duration: %" G_GUINT64_FORMAT, duration);
365   g_value_init (&start_value, G_TYPE_DOUBLE);
366   g_value_init (&end_value, G_TYPE_DOUBLE);
367   g_value_set_double (&start_value, priv->start_value);
368   g_value_set_double (&end_value, priv->end_value);
369
370   GST_LOG ("setting values on controller");
371
372   gst_interpolation_control_source_unset_all (priv->control_source);
373   gst_interpolation_control_source_set (priv->control_source, 0, &start_value);
374   gst_interpolation_control_source_set (priv->control_source,
375       duration, &end_value);
376
377   GST_LOG ("done updating controller");
378 }
379
380 /**
381  * ges_track_video_transition_set_transition_type:
382  * @self: a #GESTrackVideoTransition
383  * @type: a #GESVideoStandardTransitionType
384  *
385  * Sets the transition being used to @type.
386  *
387  * Returns: %TRUE if the transition type was properly changed, else %FALSE.
388  */
389 gboolean
390 ges_track_video_transition_set_transition_type (GESTrackVideoTransition * self,
391     GESVideoStandardTransitionType type)
392 {
393   GESTrackVideoTransitionPrivate *priv = self->priv;
394
395   GST_DEBUG ("%p %d => %d", self, priv->type, type);
396
397   if (priv->type && (priv->type != type) &&
398       ((type == GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE) ||
399           (priv->type == GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE))) {
400     GST_WARNING
401         ("Changing between 'crossfade' and other types is not supported");
402     return FALSE;
403   }
404
405   priv->type = type;
406   if (priv->smpte && (type != GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE))
407     g_object_set (priv->smpte, "type", (gint) type, NULL);
408   return TRUE;
409 }
410
411 /**
412  * ges_track_video_transition_get_transition_type:
413  * @trans: a #GESTrackVideoTransition
414  *
415  * Get the transition type used by @trans.
416  *
417  * Returns: The transition type used by @trans.
418  */
419 GESVideoStandardTransitionType
420 ges_track_video_transition_get_transition_type (GESTrackVideoTransition * trans)
421 {
422   return trans->priv->type;
423 }
424
425 /**
426  * ges_track_video_transition_new:
427  *
428  * Creates a new #GESTrackVideoTransition.
429  *
430  * Returns: The newly created #GESTrackVideoTransition, or %NULL if there was an
431  * error.
432  */
433 GESTrackVideoTransition *
434 ges_track_video_transition_new (void)
435 {
436   return g_object_new (GES_TYPE_TRACK_VIDEO_TRANSITION, NULL);
437 }