tizen 2.0 init
[framework/multimedia/gst-plugins-good0.10.git] / gst / videomixer / videomixer.c
1 /* Generic video mixer plugin
2  * Copyright (C) 2004 Wim Taymans <wim@fluendo.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:element-videomixer
22  *
23  * Videomixer can accept AYUV, ARGB and BGRA video streams. For each of the requested
24  * sink pads it will compare the incoming geometry and framerate to define the
25  * output parameters. Indeed output video frames will have the geometry of the
26  * biggest incoming video stream and the framerate of the fastest incoming one.
27  *
28  * All sink pads must be either AYUV, ARGB or BGRA, but a mixture of them is not 
29  * supported. The src pad will have the same colorspace as the sinks. 
30  * No colorspace conversion is done. 
31  *
32  * Individual parameters for each input stream can be configured on the
33  * #GstVideoMixerPad.
34  *
35  * <refsect2>
36  * <title>Sample pipelines</title>
37  * |[
38  * gst-launch-0.10 \
39  *   videotestsrc pattern=1 ! \
40  *   video/x-raw-yuv,format=\(fourcc\)AYUV,framerate=\(fraction\)10/1,width=100,height=100 ! \
41  *   videobox border-alpha=0 top=-70 bottom=-70 right=-220 ! \
42  *   videomixer name=mix sink_0::alpha=0.7 sink_1::alpha=0.5 ! \
43  *   ffmpegcolorspace ! xvimagesink \
44  *   videotestsrc ! \
45  *   video/x-raw-yuv,format=\(fourcc\)AYUV,framerate=\(fraction\)5/1,width=320,height=240 ! mix.
46  * ]| A pipeline to demonstrate videomixer used together with videobox.
47  * This should show a 320x240 pixels video test source with some transparency
48  * showing the background checker pattern. Another video test source with just
49  * the snow pattern of 100x100 pixels is overlayed on top of the first one on
50  * the left vertically centered with a small transparency showing the first
51  * video test source behind and the checker pattern under it. Note that the
52  * framerate of the output video is 10 frames per second.
53  * |[
54  * gst-launch videotestsrc pattern=1 ! \
55  *   video/x-raw-rgb, framerate=\(fraction\)10/1, width=100, height=100 ! \
56  *   videomixer name=mix ! ffmpegcolorspace ! ximagesink \
57  *   videotestsrc !  \
58  *   video/x-raw-rgb, framerate=\(fraction\)5/1, width=320, height=240 ! mix.
59  * ]| A pipeline to demostrate bgra mixing. (This does not demonstrate alpha blending). 
60  * |[
61  * gst-launch videotestsrc pattern=1 ! \
62  *   video/x-raw-yuv,format =\(fourcc\)I420, framerate=\(fraction\)10/1, width=100, height=100 ! \
63  *   videomixer name=mix ! ffmpegcolorspace ! ximagesink \
64  *   videotestsrc ! \
65  *   video/x-raw-yuv,format=\(fourcc\)I420, framerate=\(fraction\)5/1, width=320, height=240 ! mix.
66  * ]| A pipeline to test I420
67  * |[
68  * gst-launch videotestsrc pattern="snow" ! video/x-raw-yuv, framerate=\(fraction\)10/1, width=200, height=150 ! videomixer name=mix sink_1::xpos=20 sink_1::ypos=20 sink_1::alpha=0.5 ! ffmpegcolorspace ! xvimagesink videotestsrc ! video/x-raw-yuv, framerate=\(fraction\)10/1, width=640, height=360 ! mix.
69  * ]| Set position and alpha on the mixer using #GstVideoMixerPad properties.
70  * </refsect2>
71  */
72
73 #ifdef HAVE_CONFIG_H
74 #include "config.h"
75 #endif
76
77 #include <gst/gst.h>
78 #include <gst/base/gstcollectpads.h>
79 #include <gst/controller/gstcontroller.h>
80 #include <gst/video/video.h>
81
82 #ifdef HAVE_STDLIB_H
83 #include <stdlib.h>
84 #endif
85 #ifdef HAVE_STRING_H
86 #include <string.h>
87 #endif
88
89 #include "videomixer.h"
90 #include "videomixer2.h"
91
92 #include "gst/glib-compat-private.h"
93
94 #ifdef DISABLE_ORC
95 #define orc_memset memset
96 #else
97 #include <orc/orcfunctions.h>
98 #endif
99
100 GST_DEBUG_CATEGORY_STATIC (gst_videomixer_debug);
101 #define GST_CAT_DEFAULT gst_videomixer_debug
102
103 #define GST_VIDEO_MIXER_GET_STATE_LOCK(mix) \
104   (GST_VIDEO_MIXER(mix)->state_lock)
105 #define GST_VIDEO_MIXER_STATE_LOCK(mix) \
106   (g_mutex_lock(GST_VIDEO_MIXER_GET_STATE_LOCK (mix)))
107 #define GST_VIDEO_MIXER_STATE_UNLOCK(mix) \
108   (g_mutex_unlock(GST_VIDEO_MIXER_GET_STATE_LOCK (mix)))
109
110 static GType gst_videomixer_get_type (void);
111
112 static void gst_videomixer_pad_get_property (GObject * object, guint prop_id,
113     GValue * value, GParamSpec * pspec);
114 static void gst_videomixer_pad_set_property (GObject * object, guint prop_id,
115     const GValue * value, GParamSpec * pspec);
116
117 static gboolean gst_videomixer_src_event (GstPad * pad, GstEvent * event);
118 static gboolean gst_videomixer_sink_event (GstPad * pad, GstEvent * event);
119
120 static void gst_videomixer_sort_pads (GstVideoMixer * mix);
121
122 #define DEFAULT_PAD_ZORDER 0
123 #define DEFAULT_PAD_XPOS   0
124 #define DEFAULT_PAD_YPOS   0
125 #define DEFAULT_PAD_ALPHA  1.0
126 enum
127 {
128   PROP_PAD_0,
129   PROP_PAD_ZORDER,
130   PROP_PAD_XPOS,
131   PROP_PAD_YPOS,
132   PROP_PAD_ALPHA
133 };
134
135 GType gst_videomixer_pad_get_type (void);
136 G_DEFINE_TYPE (GstVideoMixerPad, gst_videomixer_pad, GST_TYPE_PAD);
137
138 static void
139 gst_videomixer_pad_class_init (GstVideoMixerPadClass * klass)
140 {
141   GObjectClass *gobject_class = (GObjectClass *) klass;
142
143   gobject_class->set_property = gst_videomixer_pad_set_property;
144   gobject_class->get_property = gst_videomixer_pad_get_property;
145
146   g_object_class_install_property (gobject_class, PROP_PAD_ZORDER,
147       g_param_spec_uint ("zorder", "Z-Order", "Z Order of the picture",
148           0, 10000, DEFAULT_PAD_ZORDER,
149           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
150   g_object_class_install_property (gobject_class, PROP_PAD_XPOS,
151       g_param_spec_int ("xpos", "X Position", "X Position of the picture",
152           G_MININT, G_MAXINT, DEFAULT_PAD_XPOS,
153           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
154   g_object_class_install_property (gobject_class, PROP_PAD_YPOS,
155       g_param_spec_int ("ypos", "Y Position", "Y Position of the picture",
156           G_MININT, G_MAXINT, DEFAULT_PAD_YPOS,
157           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
158   g_object_class_install_property (gobject_class, PROP_PAD_ALPHA,
159       g_param_spec_double ("alpha", "Alpha", "Alpha of the picture", 0.0, 1.0,
160           DEFAULT_PAD_ALPHA,
161           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
162 }
163
164 static void
165 gst_videomixer_pad_get_property (GObject * object, guint prop_id,
166     GValue * value, GParamSpec * pspec)
167 {
168   GstVideoMixerPad *pad = GST_VIDEO_MIXER_PAD (object);
169
170   switch (prop_id) {
171     case PROP_PAD_ZORDER:
172       g_value_set_uint (value, pad->zorder);
173       break;
174     case PROP_PAD_XPOS:
175       g_value_set_int (value, pad->xpos);
176       break;
177     case PROP_PAD_YPOS:
178       g_value_set_int (value, pad->ypos);
179       break;
180     case PROP_PAD_ALPHA:
181       g_value_set_double (value, pad->alpha);
182       break;
183     default:
184       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
185       break;
186   }
187 }
188
189 static void
190 gst_videomixer_pad_set_property (GObject * object, guint prop_id,
191     const GValue * value, GParamSpec * pspec)
192 {
193   GstVideoMixerPad *pad = GST_VIDEO_MIXER_PAD (object);
194   GstVideoMixer *mix = GST_VIDEO_MIXER (gst_pad_get_parent (GST_PAD (pad)));
195
196   switch (prop_id) {
197     case PROP_PAD_ZORDER:
198       GST_VIDEO_MIXER_STATE_LOCK (mix);
199       pad->zorder = g_value_get_uint (value);
200       gst_videomixer_sort_pads (mix);
201       GST_VIDEO_MIXER_STATE_UNLOCK (mix);
202       break;
203     case PROP_PAD_XPOS:
204       pad->xpos = g_value_get_int (value);
205       break;
206     case PROP_PAD_YPOS:
207       pad->ypos = g_value_get_int (value);
208       break;
209     case PROP_PAD_ALPHA:
210       pad->alpha = g_value_get_double (value);
211       break;
212     default:
213       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
214       break;
215   }
216
217   gst_object_unref (mix);
218 }
219
220 static void
221 gst_videomixer_update_qos (GstVideoMixer * mix, gdouble proportion,
222     GstClockTimeDiff diff, GstClockTime timestamp)
223 {
224   GST_DEBUG_OBJECT (mix,
225       "Updating QoS: proportion %lf, diff %s%" GST_TIME_FORMAT ", timestamp %"
226       GST_TIME_FORMAT, proportion, (diff < 0) ? "-" : "",
227       GST_TIME_ARGS (ABS (diff)), GST_TIME_ARGS (timestamp));
228
229   GST_OBJECT_LOCK (mix);
230   mix->proportion = proportion;
231   if (G_LIKELY (timestamp != GST_CLOCK_TIME_NONE)) {
232     if (G_UNLIKELY (diff > 0))
233       mix->earliest_time =
234           timestamp + 2 * diff + gst_util_uint64_scale_int (GST_SECOND,
235           mix->fps_d, mix->fps_n);
236     else
237       mix->earliest_time = timestamp + diff;
238   } else {
239     mix->earliest_time = GST_CLOCK_TIME_NONE;
240   }
241   GST_OBJECT_UNLOCK (mix);
242 }
243
244 static void
245 gst_videomixer_reset_qos (GstVideoMixer * mix)
246 {
247   gst_videomixer_update_qos (mix, 0.5, 0, GST_CLOCK_TIME_NONE);
248 }
249
250 static void
251 gst_videomixer_read_qos (GstVideoMixer * mix, gdouble * proportion,
252     GstClockTime * time)
253 {
254   GST_OBJECT_LOCK (mix);
255   *proportion = mix->proportion;
256   *time = mix->earliest_time;
257   GST_OBJECT_UNLOCK (mix);
258 }
259
260 /* Perform qos calculations before processing the next frame. Returns TRUE if
261  * the frame should be processed, FALSE if the frame can be dropped entirely */
262 static gboolean
263 gst_videomixer_do_qos (GstVideoMixer * mix, GstClockTime timestamp)
264 {
265   GstClockTime qostime, earliest_time;
266   gdouble proportion;
267
268   /* no timestamp, can't do QoS => process frame */
269   if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (timestamp))) {
270     GST_LOG_OBJECT (mix, "invalid timestamp, can't do QoS, process frame");
271     return TRUE;
272   }
273
274   /* get latest QoS observation values */
275   gst_videomixer_read_qos (mix, &proportion, &earliest_time);
276
277   /* skip qos if we have no observation (yet) => process frame */
278   if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (earliest_time))) {
279     GST_LOG_OBJECT (mix, "no observation yet, process frame");
280     return TRUE;
281   }
282
283   /* qos is done on running time */
284   qostime =
285       gst_segment_to_running_time (&mix->segment, GST_FORMAT_TIME, timestamp);
286
287   /* see how our next timestamp relates to the latest qos timestamp */
288   GST_LOG_OBJECT (mix, "qostime %" GST_TIME_FORMAT ", earliest %"
289       GST_TIME_FORMAT, GST_TIME_ARGS (qostime), GST_TIME_ARGS (earliest_time));
290
291   if (qostime != GST_CLOCK_TIME_NONE && qostime <= earliest_time) {
292     GST_DEBUG_OBJECT (mix, "we are late, drop frame");
293     return FALSE;
294   }
295
296   GST_LOG_OBJECT (mix, "process frame");
297   return TRUE;
298 }
299
300 static void
301 gst_videomixer_set_master_geometry (GstVideoMixer * mix)
302 {
303   GSList *walk;
304   gint width = 0, height = 0, fps_n = 0, fps_d = 0, par_n = 0, par_d = 0;
305   GstVideoMixerPad *master = NULL;
306
307   walk = mix->sinkpads;
308   while (walk) {
309     GstVideoMixerPad *mixpad = GST_VIDEO_MIXER_PAD (walk->data);
310
311     walk = g_slist_next (walk);
312
313     /* Biggest input geometry will be our output geometry */
314     width = MAX (width, mixpad->in_width);
315     height = MAX (height, mixpad->in_height);
316
317     /* If mix framerate < mixpad framerate, using fractions */
318     GST_DEBUG_OBJECT (mixpad, "comparing framerate %d/%d to mixpad's %d/%d",
319         fps_n, fps_d, mixpad->fps_n, mixpad->fps_d);
320     if ((!fps_n && !fps_d) ||
321         ((gint64) fps_n * mixpad->fps_d < (gint64) mixpad->fps_n * fps_d)) {
322       fps_n = mixpad->fps_n;
323       fps_d = mixpad->fps_d;
324       par_n = mixpad->par_n;
325       par_d = mixpad->par_d;
326       GST_DEBUG_OBJECT (mixpad, "becomes the master pad");
327       master = mixpad;
328     }
329   }
330
331   /* set results */
332   if (mix->master != master || mix->in_width != width
333       || mix->in_height != height || mix->fps_n != fps_n
334       || mix->fps_d != fps_d || mix->par_n != par_n || mix->par_d != par_d) {
335     mix->setcaps = TRUE;
336     mix->sendseg = TRUE;
337     gst_videomixer_reset_qos (mix);
338     mix->master = master;
339     mix->in_width = width;
340     mix->in_height = height;
341     mix->fps_n = fps_n;
342     mix->fps_d = fps_d;
343     mix->par_n = par_n;
344     mix->par_d = par_d;
345   }
346 }
347
348 static gboolean
349 gst_videomixer_pad_sink_setcaps (GstPad * pad, GstCaps * vscaps)
350 {
351   GstVideoMixer *mix;
352   GstVideoMixerPad *mixpad;
353   GstStructure *structure;
354   gint in_width, in_height;
355   gboolean ret = FALSE;
356   const GValue *framerate, *par;
357
358   GST_INFO_OBJECT (pad, "Setting caps %" GST_PTR_FORMAT, vscaps);
359
360   mix = GST_VIDEO_MIXER (gst_pad_get_parent (pad));
361   mixpad = GST_VIDEO_MIXER_PAD (pad);
362
363   if (!mixpad)
364     goto beach;
365
366   structure = gst_caps_get_structure (vscaps, 0);
367
368   if (!gst_structure_get_int (structure, "width", &in_width)
369       || !gst_structure_get_int (structure, "height", &in_height)
370       || (framerate = gst_structure_get_value (structure, "framerate")) == NULL)
371     goto beach;
372   par = gst_structure_get_value (structure, "pixel-aspect-ratio");
373
374   GST_VIDEO_MIXER_STATE_LOCK (mix);
375   mixpad->fps_n = gst_value_get_fraction_numerator (framerate);
376   mixpad->fps_d = gst_value_get_fraction_denominator (framerate);
377   if (par) {
378     mixpad->par_n = gst_value_get_fraction_numerator (par);
379     mixpad->par_d = gst_value_get_fraction_denominator (par);
380   } else {
381     mixpad->par_n = mixpad->par_d = 1;
382   }
383
384   mixpad->in_width = in_width;
385   mixpad->in_height = in_height;
386
387   gst_videomixer_set_master_geometry (mix);
388   GST_VIDEO_MIXER_STATE_UNLOCK (mix);
389
390   ret = TRUE;
391
392 beach:
393   gst_object_unref (mix);
394
395   return ret;
396 }
397
398 static GstCaps *
399 gst_videomixer_pad_sink_getcaps (GstPad * pad)
400 {
401   GstVideoMixer *mix;
402   GstVideoMixerPad *mixpad;
403   GstCaps *res = NULL;
404   GstCaps *mastercaps;
405   GstStructure *st;
406
407   mix = GST_VIDEO_MIXER (gst_pad_get_parent (pad));
408   mixpad = GST_VIDEO_MIXER_PAD (pad);
409
410   if (!mixpad)
411     goto beach;
412
413   /* Get downstream allowed caps */
414   res = gst_pad_get_allowed_caps (mix->srcpad);
415   if (G_UNLIKELY (res == NULL)) {
416     res = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
417     goto beach;
418   }
419
420   GST_VIDEO_MIXER_STATE_LOCK (mix);
421
422   /* Return as-is if not other sinkpad set as master */
423   if (mix->master == NULL) {
424     GST_VIDEO_MIXER_STATE_UNLOCK (mix);
425     goto beach;
426   }
427
428   mastercaps = gst_pad_get_fixed_caps_func (GST_PAD (mix->master));
429
430   /* If master pad caps aren't negotiated yet, return downstream
431    * allowed caps */
432   if (!GST_CAPS_IS_SIMPLE (mastercaps)) {
433     GST_VIDEO_MIXER_STATE_UNLOCK (mix);
434     gst_caps_unref (mastercaps);
435     goto beach;
436   }
437
438   gst_caps_unref (res);
439   res = gst_caps_make_writable (mastercaps);
440   st = gst_caps_get_structure (res, 0);
441   gst_structure_set (st, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
442       "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
443       "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
444   if (!gst_structure_has_field (st, "pixel-aspect-ratio"))
445     gst_structure_set (st, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, NULL);
446
447   GST_VIDEO_MIXER_STATE_UNLOCK (mix);
448
449
450 beach:
451   GST_DEBUG_OBJECT (pad, "Returning %" GST_PTR_FORMAT, res);
452
453   return res;
454 }
455
456 /*
457 * We accept the caps if it has the same format as other sink pads in 
458 * the element.
459 */
460 static gboolean
461 gst_videomixer_pad_sink_acceptcaps (GstPad * pad, GstCaps * vscaps)
462 {
463   gboolean ret;
464   GstVideoMixer *mix;
465   GstCaps *acceptedCaps;
466
467   mix = GST_VIDEO_MIXER (gst_pad_get_parent (pad));
468   GST_DEBUG_OBJECT (pad, "%" GST_PTR_FORMAT, vscaps);
469   GST_VIDEO_MIXER_STATE_LOCK (mix);
470
471   if (mix->master) {
472     acceptedCaps = gst_pad_get_fixed_caps_func (GST_PAD (mix->master));
473     acceptedCaps = gst_caps_make_writable (acceptedCaps);
474     GST_LOG_OBJECT (pad, "master's caps %" GST_PTR_FORMAT, acceptedCaps);
475     if (GST_CAPS_IS_SIMPLE (acceptedCaps)) {
476       GstStructure *s;
477       s = gst_caps_get_structure (acceptedCaps, 0);
478       gst_structure_set (s, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
479           "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
480           "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
481       if (!gst_structure_has_field (s, "pixel-aspect-ratio"))
482         gst_structure_set (s, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
483             NULL);
484     }
485   } else {
486     acceptedCaps = gst_pad_get_fixed_caps_func (pad);
487   }
488
489   GST_INFO_OBJECT (pad, "vscaps: %" GST_PTR_FORMAT, vscaps);
490   GST_INFO_OBJECT (pad, "acceptedCaps: %" GST_PTR_FORMAT, acceptedCaps);
491
492   ret = gst_caps_can_intersect (vscaps, acceptedCaps);
493   GST_INFO_OBJECT (pad, "%saccepted caps %" GST_PTR_FORMAT, (ret ? "" : "not "),
494       vscaps);
495   gst_caps_unref (acceptedCaps);
496   GST_VIDEO_MIXER_STATE_UNLOCK (mix);
497   gst_object_unref (mix);
498   return ret;
499 }
500
501
502
503 static void
504 gst_videomixer_pad_init (GstVideoMixerPad * mixerpad)
505 {
506   /* setup some pad functions */
507   gst_pad_set_setcaps_function (GST_PAD (mixerpad),
508       gst_videomixer_pad_sink_setcaps);
509   gst_pad_set_acceptcaps_function (GST_PAD (mixerpad),
510       GST_DEBUG_FUNCPTR (gst_videomixer_pad_sink_acceptcaps));
511   gst_pad_set_getcaps_function (GST_PAD (mixerpad),
512       gst_videomixer_pad_sink_getcaps);
513
514   mixerpad->zorder = DEFAULT_PAD_ZORDER;
515   mixerpad->xpos = DEFAULT_PAD_XPOS;
516   mixerpad->ypos = DEFAULT_PAD_YPOS;
517   mixerpad->alpha = DEFAULT_PAD_ALPHA;
518 }
519
520 /* VideoMixer signals and args */
521 enum
522 {
523   /* FILL ME */
524   LAST_SIGNAL
525 };
526
527 #define DEFAULT_BACKGROUND VIDEO_MIXER_BACKGROUND_CHECKER
528 enum
529 {
530   PROP_0,
531   PROP_BACKGROUND
532 };
533
534 #define GST_TYPE_VIDEO_MIXER_BACKGROUND (gst_video_mixer_background_get_type())
535 static GType
536 gst_video_mixer_background_get_type (void)
537 {
538   static GType video_mixer_background_type = 0;
539
540   static const GEnumValue video_mixer_background[] = {
541     {VIDEO_MIXER_BACKGROUND_CHECKER, "Checker pattern", "checker"},
542     {VIDEO_MIXER_BACKGROUND_BLACK, "Black", "black"},
543     {VIDEO_MIXER_BACKGROUND_WHITE, "White", "white"},
544     {VIDEO_MIXER_BACKGROUND_TRANSPARENT,
545         "Transparent Background to enable further mixing", "transparent"},
546     {0, NULL, NULL},
547   };
548
549   if (!video_mixer_background_type) {
550     video_mixer_background_type =
551         g_enum_register_static ("GstVideoMixerBackground",
552         video_mixer_background);
553   }
554   return video_mixer_background_type;
555 }
556
557 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
558     GST_PAD_SRC,
559     GST_PAD_ALWAYS,
560     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";" GST_VIDEO_CAPS_BGRA ";"
561         GST_VIDEO_CAPS_ARGB ";" GST_VIDEO_CAPS_RGBA ";" GST_VIDEO_CAPS_ABGR ";"
562         GST_VIDEO_CAPS_YUV ("Y444") ";" GST_VIDEO_CAPS_YUV ("Y42B") ";"
563         GST_VIDEO_CAPS_YUV ("YUY2") ";" GST_VIDEO_CAPS_YUV ("UYVY") ";"
564         GST_VIDEO_CAPS_YUV ("YVYU") ";"
565         GST_VIDEO_CAPS_YUV ("I420") ";" GST_VIDEO_CAPS_YUV ("YV12") ";"
566         GST_VIDEO_CAPS_YUV ("Y41B") ";" GST_VIDEO_CAPS_RGB ";"
567         GST_VIDEO_CAPS_BGR ";" GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_xBGR ";"
568         GST_VIDEO_CAPS_RGBx ";" GST_VIDEO_CAPS_BGRx)
569     );
570
571 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%d",
572     GST_PAD_SINK,
573     GST_PAD_REQUEST,
574     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";" GST_VIDEO_CAPS_BGRA ";"
575         GST_VIDEO_CAPS_ARGB ";" GST_VIDEO_CAPS_RGBA ";" GST_VIDEO_CAPS_ABGR ";"
576         GST_VIDEO_CAPS_YUV ("Y444") ";" GST_VIDEO_CAPS_YUV ("Y42B") ";"
577         GST_VIDEO_CAPS_YUV ("YUY2") ";" GST_VIDEO_CAPS_YUV ("UYVY") ";"
578         GST_VIDEO_CAPS_YUV ("YVYU") ";"
579         GST_VIDEO_CAPS_YUV ("I420") ";" GST_VIDEO_CAPS_YUV ("YV12") ";"
580         GST_VIDEO_CAPS_YUV ("Y41B") ";" GST_VIDEO_CAPS_RGB ";"
581         GST_VIDEO_CAPS_BGR ";" GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_xBGR ";"
582         GST_VIDEO_CAPS_RGBx ";" GST_VIDEO_CAPS_BGRx)
583     );
584
585 static void gst_videomixer_finalize (GObject * object);
586
587 static GstCaps *gst_videomixer_getcaps (GstPad * pad);
588 static gboolean gst_videomixer_setcaps (GstPad * pad, GstCaps * caps);
589 static gboolean gst_videomixer_query (GstPad * pad, GstQuery * query);
590
591 static GstFlowReturn gst_videomixer_collected (GstCollectPads * pads,
592     GstVideoMixer * mix);
593 static GstPad *gst_videomixer_request_new_pad (GstElement * element,
594     GstPadTemplate * templ, const gchar * name);
595 static void gst_videomixer_release_pad (GstElement * element, GstPad * pad);
596
597 static void gst_videomixer_set_property (GObject * object, guint prop_id,
598     const GValue * value, GParamSpec * pspec);
599 static void gst_videomixer_get_property (GObject * object, guint prop_id,
600     GValue * value, GParamSpec * pspec);
601 static GstStateChangeReturn gst_videomixer_change_state (GstElement * element,
602     GstStateChange transition);
603
604 /*static guint gst_videomixer_signals[LAST_SIGNAL] = { 0 }; */
605
606 static void gst_videomixer_child_proxy_init (gpointer g_iface,
607     gpointer iface_data);
608 static void _do_init (GType object_type);
609
610 GST_BOILERPLATE_FULL (GstVideoMixer, gst_videomixer, GstElement,
611     GST_TYPE_ELEMENT, _do_init);
612
613 static void
614 _do_init (GType object_type)
615 {
616   static const GInterfaceInfo child_proxy_info = {
617     (GInterfaceInitFunc) gst_videomixer_child_proxy_init,
618     NULL,
619     NULL
620   };
621
622   g_type_add_interface_static (object_type, GST_TYPE_CHILD_PROXY,
623       &child_proxy_info);
624   GST_INFO ("GstChildProxy interface registered");
625 }
626
627 static GstObject *
628 gst_videomixer_child_proxy_get_child_by_index (GstChildProxy * child_proxy,
629     guint index)
630 {
631   GstVideoMixer *mix = GST_VIDEO_MIXER (child_proxy);
632   GstObject *obj;
633
634   GST_VIDEO_MIXER_STATE_LOCK (mix);
635   if ((obj = g_slist_nth_data (mix->sinkpads, index)))
636     gst_object_ref (obj);
637   GST_VIDEO_MIXER_STATE_UNLOCK (mix);
638   return obj;
639 }
640
641 static guint
642 gst_videomixer_child_proxy_get_children_count (GstChildProxy * child_proxy)
643 {
644   guint count = 0;
645   GstVideoMixer *mix = GST_VIDEO_MIXER (child_proxy);
646
647   GST_VIDEO_MIXER_STATE_LOCK (mix);
648   count = mix->numpads;
649   GST_VIDEO_MIXER_STATE_UNLOCK (mix);
650   GST_INFO_OBJECT (mix, "Children Count: %d", count);
651   return count;
652 }
653
654 static void
655 gst_videomixer_child_proxy_init (gpointer g_iface, gpointer iface_data)
656 {
657   GstChildProxyInterface *iface = g_iface;
658
659   GST_INFO ("intializing child proxy interface");
660   iface->get_child_by_index = gst_videomixer_child_proxy_get_child_by_index;
661   iface->get_children_count = gst_videomixer_child_proxy_get_children_count;
662 }
663
664 static void
665 gst_videomixer_base_init (gpointer g_class)
666 {
667   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
668
669   gst_element_class_add_static_pad_template (element_class, &src_factory);
670   gst_element_class_add_static_pad_template (element_class, &sink_factory);
671
672   gst_element_class_set_details_simple (element_class, "Video mixer",
673       "Filter/Editor/Video",
674       "Mix multiple video streams", "Wim Taymans <wim@fluendo.com>");
675 }
676
677 static void
678 gst_videomixer_class_init (GstVideoMixerClass * klass)
679 {
680   GObjectClass *gobject_class = (GObjectClass *) klass;
681   GstElementClass *gstelement_class = (GstElementClass *) klass;
682
683   gobject_class->finalize = gst_videomixer_finalize;
684
685   gobject_class->get_property = gst_videomixer_get_property;
686   gobject_class->set_property = gst_videomixer_set_property;
687
688   g_object_class_install_property (gobject_class, PROP_BACKGROUND,
689       g_param_spec_enum ("background", "Background", "Background type",
690           GST_TYPE_VIDEO_MIXER_BACKGROUND,
691           DEFAULT_BACKGROUND, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
692
693   gstelement_class->request_new_pad =
694       GST_DEBUG_FUNCPTR (gst_videomixer_request_new_pad);
695   gstelement_class->release_pad =
696       GST_DEBUG_FUNCPTR (gst_videomixer_release_pad);
697   gstelement_class->change_state =
698       GST_DEBUG_FUNCPTR (gst_videomixer_change_state);
699
700   /* Register the pad class */
701   (void) (GST_TYPE_VIDEO_MIXER_PAD);
702   /* Register the background enum */
703   (void) (GST_TYPE_VIDEO_MIXER_BACKGROUND);
704 }
705
706 static void
707 gst_videomixer_collect_free (GstVideoMixerCollect * mixcol)
708 {
709   if (mixcol->buffer) {
710     gst_buffer_unref (mixcol->buffer);
711     mixcol->buffer = NULL;
712   }
713 }
714
715 static void
716 gst_videomixer_reset (GstVideoMixer * mix)
717 {
718   GSList *walk;
719
720   mix->in_width = 0;
721   mix->in_height = 0;
722   mix->out_width = 0;
723   mix->out_height = 0;
724   mix->fps_n = mix->fps_d = 0;
725   mix->par_n = mix->par_d = 1;
726   mix->setcaps = FALSE;
727   mix->sendseg = FALSE;
728
729   mix->segment_position = 0;
730   gst_segment_init (&mix->segment, GST_FORMAT_TIME);
731
732   gst_videomixer_reset_qos (mix);
733
734   mix->fmt = GST_VIDEO_FORMAT_UNKNOWN;
735
736   mix->last_ts = 0;
737   mix->last_duration = -1;
738
739   /* clean up collect data */
740   walk = mix->collect->data;
741   while (walk) {
742     GstVideoMixerCollect *data = (GstVideoMixerCollect *) walk->data;
743
744     gst_videomixer_collect_free (data);
745     walk = g_slist_next (walk);
746   }
747
748   mix->next_sinkpad = 0;
749   mix->flush_stop_pending = FALSE;
750 }
751
752 static void
753 gst_videomixer_init (GstVideoMixer * mix, GstVideoMixerClass * g_class)
754 {
755   GstElementClass *klass = GST_ELEMENT_GET_CLASS (mix);
756
757   mix->srcpad =
758       gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
759           "src"), "src");
760   gst_pad_set_getcaps_function (GST_PAD (mix->srcpad),
761       GST_DEBUG_FUNCPTR (gst_videomixer_getcaps));
762   gst_pad_set_setcaps_function (GST_PAD (mix->srcpad),
763       GST_DEBUG_FUNCPTR (gst_videomixer_setcaps));
764   gst_pad_set_query_function (GST_PAD (mix->srcpad),
765       GST_DEBUG_FUNCPTR (gst_videomixer_query));
766   gst_pad_set_event_function (GST_PAD (mix->srcpad),
767       GST_DEBUG_FUNCPTR (gst_videomixer_src_event));
768   gst_element_add_pad (GST_ELEMENT (mix), mix->srcpad);
769
770   mix->collect = gst_collect_pads_new ();
771   mix->background = DEFAULT_BACKGROUND;
772
773   gst_collect_pads_set_function (mix->collect,
774       (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_videomixer_collected),
775       mix);
776
777   mix->state_lock = g_mutex_new ();
778   /* initialize variables */
779   gst_videomixer_reset (mix);
780 }
781
782 static void
783 gst_videomixer_finalize (GObject * object)
784 {
785   GstVideoMixer *mix = GST_VIDEO_MIXER (object);
786
787   gst_object_unref (mix->collect);
788   g_mutex_free (mix->state_lock);
789
790   G_OBJECT_CLASS (parent_class)->finalize (object);
791 }
792
793 static gboolean
794 gst_videomixer_query_duration (GstVideoMixer * mix, GstQuery * query)
795 {
796   gint64 max;
797   gboolean res;
798   GstFormat format;
799   GstIterator *it;
800   gboolean done;
801
802   /* parse format */
803   gst_query_parse_duration (query, &format, NULL);
804
805   max = -1;
806   res = TRUE;
807   done = FALSE;
808
809   /* Take maximum of all durations */
810   it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (mix));
811   while (!done) {
812     GstIteratorResult ires;
813     gpointer item;
814
815     ires = gst_iterator_next (it, &item);
816     switch (ires) {
817       case GST_ITERATOR_DONE:
818         done = TRUE;
819         break;
820       case GST_ITERATOR_OK:
821       {
822         GstPad *pad = GST_PAD_CAST (item);
823         gint64 duration;
824
825         /* ask sink peer for duration */
826         res &= gst_pad_query_peer_duration (pad, &format, &duration);
827         /* take max from all valid return values */
828         if (res) {
829           /* valid unknown length, stop searching */
830           if (duration == -1) {
831             max = duration;
832             done = TRUE;
833           }
834           /* else see if bigger than current max */
835           else if (duration > max)
836             max = duration;
837         }
838         gst_object_unref (pad);
839         break;
840       }
841       case GST_ITERATOR_RESYNC:
842         max = -1;
843         res = TRUE;
844         gst_iterator_resync (it);
845         break;
846       default:
847         res = FALSE;
848         done = TRUE;
849         break;
850     }
851   }
852   gst_iterator_free (it);
853
854   if (res) {
855     /* and store the max */
856     GST_DEBUG_OBJECT (mix, "Total duration in format %s: %"
857         GST_TIME_FORMAT, gst_format_get_name (format), GST_TIME_ARGS (max));
858     gst_query_set_duration (query, format, max);
859   }
860
861   return res;
862 }
863
864 static gboolean
865 gst_videomixer_query_latency (GstVideoMixer * mix, GstQuery * query)
866 {
867   GstClockTime min, max;
868   gboolean live;
869   gboolean res;
870   GstIterator *it;
871   gboolean done;
872
873   res = TRUE;
874   done = FALSE;
875   live = FALSE;
876   min = 0;
877   max = GST_CLOCK_TIME_NONE;
878
879   /* Take maximum of all latency values */
880   it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (mix));
881   while (!done) {
882     GstIteratorResult ires;
883     gpointer item;
884
885     ires = gst_iterator_next (it, &item);
886     switch (ires) {
887       case GST_ITERATOR_DONE:
888         done = TRUE;
889         break;
890       case GST_ITERATOR_OK:
891       {
892         GstPad *pad = GST_PAD_CAST (item);
893
894         GstQuery *peerquery;
895
896         GstClockTime min_cur, max_cur;
897
898         gboolean live_cur;
899
900         peerquery = gst_query_new_latency ();
901
902         /* Ask peer for latency */
903         res &= gst_pad_peer_query (pad, peerquery);
904
905         /* take max from all valid return values */
906         if (res) {
907           gst_query_parse_latency (peerquery, &live_cur, &min_cur, &max_cur);
908
909           if (min_cur > min)
910             min = min_cur;
911
912           if (max_cur != GST_CLOCK_TIME_NONE &&
913               ((max != GST_CLOCK_TIME_NONE && max_cur > max) ||
914                   (max == GST_CLOCK_TIME_NONE)))
915             max = max_cur;
916
917           live = live || live_cur;
918         }
919
920         gst_query_unref (peerquery);
921         gst_object_unref (pad);
922         break;
923       }
924       case GST_ITERATOR_RESYNC:
925         live = FALSE;
926         min = 0;
927         max = GST_CLOCK_TIME_NONE;
928         res = TRUE;
929         gst_iterator_resync (it);
930         break;
931       default:
932         res = FALSE;
933         done = TRUE;
934         break;
935     }
936   }
937   gst_iterator_free (it);
938
939   if (res) {
940     /* store the results */
941     GST_DEBUG_OBJECT (mix, "Calculated total latency: live %s, min %"
942         GST_TIME_FORMAT ", max %" GST_TIME_FORMAT,
943         (live ? "yes" : "no"), GST_TIME_ARGS (min), GST_TIME_ARGS (max));
944     gst_query_set_latency (query, live, min, max);
945   }
946
947   return res;
948 }
949
950 static gboolean
951 gst_videomixer_query (GstPad * pad, GstQuery * query)
952 {
953   GstVideoMixer *mix = GST_VIDEO_MIXER (gst_pad_get_parent (pad));
954   gboolean res = FALSE;
955
956   switch (GST_QUERY_TYPE (query)) {
957     case GST_QUERY_POSITION:
958     {
959       GstFormat format;
960
961       gst_query_parse_position (query, &format, NULL);
962
963       switch (format) {
964         case GST_FORMAT_TIME:
965           /* FIXME, bring to stream time, might be tricky */
966           gst_query_set_position (query, format, mix->last_ts);
967           res = TRUE;
968           break;
969         default:
970           break;
971       }
972       break;
973     }
974     case GST_QUERY_DURATION:
975       res = gst_videomixer_query_duration (mix, query);
976       break;
977     case GST_QUERY_LATENCY:
978       res = gst_videomixer_query_latency (mix, query);
979       break;
980     default:
981       /* FIXME, needs a custom query handler because we have multiple
982        * sinkpads, send to the master pad until then */
983       res = gst_pad_query (GST_PAD_CAST (mix->master), query);
984       break;
985   }
986
987   gst_object_unref (mix);
988   return res;
989 }
990
991 static GstCaps *
992 gst_videomixer_getcaps (GstPad * pad)
993 {
994   GstVideoMixer *mix = GST_VIDEO_MIXER (gst_pad_get_parent (pad));
995   GstCaps *caps;
996   GstStructure *structure;
997   int numCaps;
998
999   if (mix->master) {
1000     caps =
1001         gst_caps_copy (gst_pad_get_pad_template_caps (GST_PAD (mix->master)));
1002   } else {
1003     caps = gst_caps_copy (gst_pad_get_pad_template_caps (mix->srcpad));
1004   }
1005
1006   numCaps = gst_caps_get_size (caps) - 1;
1007   for (; numCaps >= 0; numCaps--) {
1008     structure = gst_caps_get_structure (caps, numCaps);
1009     if (mix->out_width != 0) {
1010       gst_structure_set (structure, "width", G_TYPE_INT, mix->out_width, NULL);
1011     }
1012     if (mix->out_height != 0) {
1013       gst_structure_set (structure, "height", G_TYPE_INT, mix->out_height,
1014           NULL);
1015     }
1016     if (mix->fps_d != 0) {
1017       gst_structure_set (structure,
1018           "framerate", GST_TYPE_FRACTION, mix->fps_n, mix->fps_d, NULL);
1019     }
1020   }
1021
1022   gst_object_unref (mix);
1023
1024   return caps;
1025 }
1026
1027 static gboolean
1028 gst_videomixer_setcaps (GstPad * pad, GstCaps * caps)
1029 {
1030   GstVideoMixer *mixer = GST_VIDEO_MIXER (gst_pad_get_parent_element (pad));
1031   gboolean ret = FALSE;
1032
1033   GST_INFO_OBJECT (mixer, "set src caps: %" GST_PTR_FORMAT, caps);
1034
1035   mixer->blend = NULL;
1036   mixer->overlay = NULL;
1037   mixer->fill_checker = NULL;
1038   mixer->fill_color = NULL;
1039
1040   if (!gst_video_format_parse_caps (caps, &mixer->fmt, NULL, NULL))
1041     goto done;
1042
1043   switch (mixer->fmt) {
1044     case GST_VIDEO_FORMAT_AYUV:
1045       mixer->blend = gst_video_mixer_blend_ayuv;
1046       mixer->overlay = gst_video_mixer_overlay_ayuv;
1047       mixer->fill_checker = gst_video_mixer_fill_checker_ayuv;
1048       mixer->fill_color = gst_video_mixer_fill_color_ayuv;
1049       ret = TRUE;
1050       break;
1051     case GST_VIDEO_FORMAT_ARGB:
1052       mixer->blend = gst_video_mixer_blend_argb;
1053       mixer->overlay = gst_video_mixer_overlay_argb;
1054       mixer->fill_checker = gst_video_mixer_fill_checker_argb;
1055       mixer->fill_color = gst_video_mixer_fill_color_argb;
1056       ret = TRUE;
1057       break;
1058     case GST_VIDEO_FORMAT_BGRA:
1059       mixer->blend = gst_video_mixer_blend_bgra;
1060       mixer->overlay = gst_video_mixer_overlay_bgra;
1061       mixer->fill_checker = gst_video_mixer_fill_checker_bgra;
1062       mixer->fill_color = gst_video_mixer_fill_color_bgra;
1063       ret = TRUE;
1064       break;
1065     case GST_VIDEO_FORMAT_ABGR:
1066       mixer->blend = gst_video_mixer_blend_abgr;
1067       mixer->overlay = gst_video_mixer_overlay_abgr;
1068       mixer->fill_checker = gst_video_mixer_fill_checker_abgr;
1069       mixer->fill_color = gst_video_mixer_fill_color_abgr;
1070       ret = TRUE;
1071       break;
1072     case GST_VIDEO_FORMAT_RGBA:
1073       mixer->blend = gst_video_mixer_blend_rgba;
1074       mixer->overlay = gst_video_mixer_overlay_rgba;
1075       mixer->fill_checker = gst_video_mixer_fill_checker_rgba;
1076       mixer->fill_color = gst_video_mixer_fill_color_rgba;
1077       ret = TRUE;
1078       break;
1079     case GST_VIDEO_FORMAT_Y444:
1080       mixer->blend = gst_video_mixer_blend_y444;
1081       mixer->overlay = mixer->blend;
1082       mixer->fill_checker = gst_video_mixer_fill_checker_y444;
1083       mixer->fill_color = gst_video_mixer_fill_color_y444;
1084       ret = TRUE;
1085       break;
1086     case GST_VIDEO_FORMAT_Y42B:
1087       mixer->blend = gst_video_mixer_blend_y42b;
1088       mixer->overlay = mixer->blend;
1089       mixer->fill_checker = gst_video_mixer_fill_checker_y42b;
1090       mixer->fill_color = gst_video_mixer_fill_color_y42b;
1091       ret = TRUE;
1092       break;
1093     case GST_VIDEO_FORMAT_YUY2:
1094       mixer->blend = gst_video_mixer_blend_yuy2;
1095       mixer->overlay = mixer->blend;
1096       mixer->fill_checker = gst_video_mixer_fill_checker_yuy2;
1097       mixer->fill_color = gst_video_mixer_fill_color_yuy2;
1098       ret = TRUE;
1099       break;
1100     case GST_VIDEO_FORMAT_UYVY:
1101       mixer->blend = gst_video_mixer_blend_uyvy;
1102       mixer->overlay = mixer->blend;
1103       mixer->fill_checker = gst_video_mixer_fill_checker_uyvy;
1104       mixer->fill_color = gst_video_mixer_fill_color_uyvy;
1105       ret = TRUE;
1106       break;
1107     case GST_VIDEO_FORMAT_YVYU:
1108       mixer->blend = gst_video_mixer_blend_yvyu;
1109       mixer->overlay = mixer->blend;
1110       mixer->fill_checker = gst_video_mixer_fill_checker_yvyu;
1111       mixer->fill_color = gst_video_mixer_fill_color_yvyu;
1112       ret = TRUE;
1113       break;
1114     case GST_VIDEO_FORMAT_I420:
1115       mixer->blend = gst_video_mixer_blend_i420;
1116       mixer->overlay = mixer->blend;
1117       mixer->fill_checker = gst_video_mixer_fill_checker_i420;
1118       mixer->fill_color = gst_video_mixer_fill_color_i420;
1119       ret = TRUE;
1120       break;
1121     case GST_VIDEO_FORMAT_YV12:
1122       mixer->blend = gst_video_mixer_blend_yv12;
1123       mixer->overlay = mixer->blend;
1124       mixer->fill_checker = gst_video_mixer_fill_checker_yv12;
1125       mixer->fill_color = gst_video_mixer_fill_color_yv12;
1126       ret = TRUE;
1127       break;
1128     case GST_VIDEO_FORMAT_Y41B:
1129       mixer->blend = gst_video_mixer_blend_y41b;
1130       mixer->overlay = mixer->blend;
1131       mixer->fill_checker = gst_video_mixer_fill_checker_y41b;
1132       mixer->fill_color = gst_video_mixer_fill_color_y41b;
1133       ret = TRUE;
1134       break;
1135     case GST_VIDEO_FORMAT_RGB:
1136       mixer->blend = gst_video_mixer_blend_rgb;
1137       mixer->overlay = mixer->blend;
1138       mixer->fill_checker = gst_video_mixer_fill_checker_rgb;
1139       mixer->fill_color = gst_video_mixer_fill_color_rgb;
1140       ret = TRUE;
1141       break;
1142     case GST_VIDEO_FORMAT_BGR:
1143       mixer->blend = gst_video_mixer_blend_bgr;
1144       mixer->overlay = mixer->blend;
1145       mixer->fill_checker = gst_video_mixer_fill_checker_bgr;
1146       mixer->fill_color = gst_video_mixer_fill_color_bgr;
1147       ret = TRUE;
1148       break;
1149     case GST_VIDEO_FORMAT_xRGB:
1150       mixer->blend = gst_video_mixer_blend_xrgb;
1151       mixer->overlay = mixer->blend;
1152       mixer->fill_checker = gst_video_mixer_fill_checker_xrgb;
1153       mixer->fill_color = gst_video_mixer_fill_color_xrgb;
1154       ret = TRUE;
1155       break;
1156     case GST_VIDEO_FORMAT_xBGR:
1157       mixer->blend = gst_video_mixer_blend_xbgr;
1158       mixer->overlay = mixer->blend;
1159       mixer->fill_checker = gst_video_mixer_fill_checker_xbgr;
1160       mixer->fill_color = gst_video_mixer_fill_color_xbgr;
1161       ret = TRUE;
1162       break;
1163     case GST_VIDEO_FORMAT_RGBx:
1164       mixer->blend = gst_video_mixer_blend_rgbx;
1165       mixer->overlay = mixer->blend;
1166       mixer->fill_checker = gst_video_mixer_fill_checker_rgbx;
1167       mixer->fill_color = gst_video_mixer_fill_color_rgbx;
1168       ret = TRUE;
1169       break;
1170     case GST_VIDEO_FORMAT_BGRx:
1171       mixer->blend = gst_video_mixer_blend_bgrx;
1172       mixer->overlay = mixer->blend;
1173       mixer->fill_checker = gst_video_mixer_fill_checker_bgrx;
1174       mixer->fill_color = gst_video_mixer_fill_color_bgrx;
1175       ret = TRUE;
1176       break;
1177     default:
1178       break;
1179   }
1180
1181 done:
1182   gst_object_unref (mixer);
1183
1184   return ret;
1185 }
1186
1187 static GstPad *
1188 gst_videomixer_request_new_pad (GstElement * element,
1189     GstPadTemplate * templ, const gchar * req_name)
1190 {
1191   GstVideoMixer *mix = NULL;
1192   GstVideoMixerPad *mixpad = NULL;
1193   GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1194
1195   g_return_val_if_fail (templ != NULL, NULL);
1196
1197   if (G_UNLIKELY (templ->direction != GST_PAD_SINK)) {
1198     g_warning ("videomixer: request pad that is not a SINK pad");
1199     return NULL;
1200   }
1201
1202   g_return_val_if_fail (GST_IS_VIDEO_MIXER (element), NULL);
1203
1204   mix = GST_VIDEO_MIXER (element);
1205
1206   if (templ == gst_element_class_get_pad_template (klass, "sink_%d")) {
1207     gint serial = 0;
1208     gchar *name = NULL;
1209     GstVideoMixerCollect *mixcol = NULL;
1210
1211     GST_VIDEO_MIXER_STATE_LOCK (mix);
1212     if (req_name == NULL || strlen (req_name) < 6
1213         || !g_str_has_prefix (req_name, "sink_")) {
1214       /* no name given when requesting the pad, use next available int */
1215       serial = mix->next_sinkpad++;
1216     } else {
1217       /* parse serial number from requested padname */
1218       serial = atoi (&req_name[5]);
1219       if (serial >= mix->next_sinkpad)
1220         mix->next_sinkpad = serial + 1;
1221     }
1222     /* create new pad with the name */
1223     name = g_strdup_printf ("sink_%d", serial);
1224     mixpad = g_object_new (GST_TYPE_VIDEO_MIXER_PAD, "name", name, "direction",
1225         templ->direction, "template", templ, NULL);
1226     g_free (name);
1227
1228     mixpad->zorder = mix->numpads;
1229     mixpad->xpos = DEFAULT_PAD_XPOS;
1230     mixpad->ypos = DEFAULT_PAD_YPOS;
1231     mixpad->alpha = DEFAULT_PAD_ALPHA;
1232
1233     mixcol = (GstVideoMixerCollect *)
1234         gst_collect_pads_add_pad (mix->collect, GST_PAD (mixpad),
1235         sizeof (GstVideoMixerCollect));
1236
1237     /* FIXME: hacked way to override/extend the event function of
1238      * GstCollectPads; because it sets its own event function giving the
1239      * element no access to events */
1240     mix->collect_event =
1241         (GstPadEventFunction) GST_PAD_EVENTFUNC (GST_PAD (mixpad));
1242     gst_pad_set_event_function (GST_PAD (mixpad),
1243         GST_DEBUG_FUNCPTR (gst_videomixer_sink_event));
1244
1245     /* Keep track of each other */
1246     mixcol->mixpad = mixpad;
1247     mixpad->mixcol = mixcol;
1248
1249     /* Keep an internal list of mixpads for zordering */
1250     mix->sinkpads = g_slist_append (mix->sinkpads, mixpad);
1251     mix->numpads++;
1252     GST_VIDEO_MIXER_STATE_UNLOCK (mix);
1253   } else {
1254     g_warning ("videomixer: this is not our template!");
1255     return NULL;
1256   }
1257
1258   /* add the pad to the element */
1259   gst_element_add_pad (element, GST_PAD (mixpad));
1260   gst_child_proxy_child_added (GST_OBJECT (mix), GST_OBJECT (mixpad));
1261
1262   return GST_PAD (mixpad);
1263 }
1264
1265 static void
1266 gst_videomixer_release_pad (GstElement * element, GstPad * pad)
1267 {
1268   GstVideoMixer *mix = NULL;
1269   GstVideoMixerPad *mixpad;
1270
1271   mix = GST_VIDEO_MIXER (element);
1272   GST_VIDEO_MIXER_STATE_LOCK (mix);
1273   if (G_UNLIKELY (g_slist_find (mix->sinkpads, pad) == NULL)) {
1274     g_warning ("Unknown pad %s", GST_PAD_NAME (pad));
1275     goto error;
1276   }
1277
1278   mixpad = GST_VIDEO_MIXER_PAD (pad);
1279
1280   mix->sinkpads = g_slist_remove (mix->sinkpads, pad);
1281   gst_videomixer_collect_free (mixpad->mixcol);
1282   gst_collect_pads_remove_pad (mix->collect, pad);
1283   gst_child_proxy_child_removed (GST_OBJECT (mix), GST_OBJECT (mixpad));
1284   /* determine possibly new geometry and master */
1285   gst_videomixer_set_master_geometry (mix);
1286   mix->numpads--;
1287   GST_VIDEO_MIXER_STATE_UNLOCK (mix);
1288
1289   gst_element_remove_pad (element, pad);
1290   return;
1291 error:
1292   GST_VIDEO_MIXER_STATE_UNLOCK (mix);
1293 }
1294
1295 static int
1296 pad_zorder_compare (const GstVideoMixerPad * pad1,
1297     const GstVideoMixerPad * pad2)
1298 {
1299   return pad1->zorder - pad2->zorder;
1300 }
1301
1302 static void
1303 gst_videomixer_sort_pads (GstVideoMixer * mix)
1304 {
1305   mix->sinkpads = g_slist_sort (mix->sinkpads,
1306       (GCompareFunc) pad_zorder_compare);
1307 }
1308
1309 /* try to get a buffer on all pads. As long as the queued value is
1310  * negative, we skip buffers */
1311 static gboolean
1312 gst_videomixer_fill_queues (GstVideoMixer * mix)
1313 {
1314   GSList *walk = NULL;
1315   gboolean eos = TRUE;
1316
1317   g_return_val_if_fail (GST_IS_VIDEO_MIXER (mix), FALSE);
1318
1319   /* try to make sure we have a buffer from each usable pad first */
1320   walk = mix->collect->data;
1321   while (walk) {
1322     GstCollectData *data = (GstCollectData *) walk->data;
1323     GstVideoMixerCollect *mixcol = (GstVideoMixerCollect *) data;
1324     GstVideoMixerPad *mixpad = mixcol->mixpad;
1325
1326     walk = g_slist_next (walk);
1327
1328     if (mixcol->buffer == NULL) {
1329       GstBuffer *buf = NULL;
1330
1331       GST_LOG_OBJECT (mix, "we need a new buffer");
1332
1333       buf = gst_collect_pads_peek (mix->collect, data);
1334
1335       if (buf) {
1336         guint64 duration;
1337
1338         mixcol->buffer = buf;
1339         duration = GST_BUFFER_DURATION (mixcol->buffer);
1340
1341         GST_LOG_OBJECT (mix, "we have a buffer with duration %" GST_TIME_FORMAT
1342             ", queued %" GST_TIME_FORMAT, GST_TIME_ARGS (duration),
1343             GST_TIME_ARGS (mixpad->queued));
1344
1345         /* no duration on the buffer, use the framerate */
1346         if (!GST_CLOCK_TIME_IS_VALID (duration)) {
1347           if (mixpad->fps_n == 0) {
1348             duration = GST_CLOCK_TIME_NONE;
1349           } else {
1350             duration =
1351                 gst_util_uint64_scale_int (GST_SECOND, mixpad->fps_d,
1352                 mixpad->fps_n);
1353           }
1354         }
1355         if (GST_CLOCK_TIME_IS_VALID (duration))
1356           mixpad->queued += duration;
1357         else if (!mixpad->queued)
1358           mixpad->queued = GST_CLOCK_TIME_NONE;
1359
1360         GST_LOG_OBJECT (mix, "now queued: %" GST_TIME_FORMAT,
1361             GST_TIME_ARGS (mixpad->queued));
1362       } else {
1363         GST_LOG_OBJECT (mix, "pop returned a NULL buffer");
1364       }
1365     }
1366     if (mix->sendseg && (mixpad == mix->master)) {
1367       GstEvent *event;
1368       gint64 stop, start;
1369       GstSegment *segment = &data->segment;
1370
1371       /* FIXME, use rate/applied_rate as set on all sinkpads.
1372        * - currently we just set rate as received from last seek-event
1373        * We could potentially figure out the duration as well using
1374        * the current segment positions and the stated stop positions.
1375        * Also we just start from stream time 0 which is rather
1376        * weird. For non-synchronized mixing, the time should be
1377        * the min of the stream times of all received segments,
1378        * rationale being that the duration is at least going to
1379        * be as long as the earliest stream we start mixing. This
1380        * would also be correct for synchronized mixing but then
1381        * the later streams would be delayed until the stream times
1382        * match.
1383        */
1384       GST_INFO_OBJECT (mix, "_sending play segment");
1385
1386       start = segment->accum;
1387
1388       /* get the duration of the segment if we can and add it to the accumulated
1389        * time on the segment. */
1390       if (segment->stop != -1 && segment->start != -1)
1391         stop = start + (segment->stop - segment->start);
1392       else
1393         stop = -1;
1394
1395       gst_segment_set_newsegment (&mix->segment, FALSE, segment->rate,
1396           segment->format, start, stop, start + mix->segment_position);
1397       event =
1398           gst_event_new_new_segment_full (FALSE, segment->rate, 1.0,
1399           segment->format, start, stop, start + mix->segment_position);
1400       gst_pad_push_event (mix->srcpad, event);
1401       mix->sendseg = FALSE;
1402     }
1403
1404     if (mixcol->buffer != NULL && GST_CLOCK_TIME_IS_VALID (mixpad->queued)) {
1405       /* got a buffer somewhere so we're not eos */
1406       eos = FALSE;
1407     }
1408   }
1409
1410   return eos;
1411 }
1412
1413 /* blend all buffers present on the pads */
1414 static void
1415 gst_videomixer_blend_buffers (GstVideoMixer * mix, GstBuffer * outbuf)
1416 {
1417   GSList *walk;
1418   BlendFunction blend;
1419   if (mix->background == VIDEO_MIXER_BACKGROUND_TRANSPARENT) {
1420     blend = mix->overlay;
1421   } else {
1422     blend = mix->blend;
1423   }
1424
1425
1426   walk = mix->sinkpads;
1427   while (walk) {                /* We walk with this list because it's ordered */
1428     GstVideoMixerPad *pad = GST_VIDEO_MIXER_PAD (walk->data);
1429     GstVideoMixerCollect *mixcol = pad->mixcol;
1430
1431     walk = g_slist_next (walk);
1432
1433     if (mixcol->buffer != NULL) {
1434       GstClockTime timestamp;
1435       gint64 stream_time;
1436       GstSegment *seg;
1437
1438       seg = &mixcol->collect.segment;
1439
1440       timestamp = GST_BUFFER_TIMESTAMP (mixcol->buffer);
1441
1442       stream_time =
1443           gst_segment_to_stream_time (seg, GST_FORMAT_TIME, timestamp);
1444
1445       /* sync object properties on stream time */
1446       if (GST_CLOCK_TIME_IS_VALID (stream_time))
1447         gst_object_sync_values (G_OBJECT (pad), stream_time);
1448
1449       blend (GST_BUFFER_DATA (mixcol->buffer),
1450           pad->xpos, pad->ypos, pad->in_width, pad->in_height, pad->alpha,
1451           GST_BUFFER_DATA (outbuf), mix->out_width, mix->out_height);
1452     }
1453   }
1454 }
1455
1456 /* remove buffers from the queue that were expired in the
1457  * interval of the master, we also prepare the queued value
1458  * in the pad so that we can skip and fill buffers later on */
1459 static void
1460 gst_videomixer_update_queues (GstVideoMixer * mix)
1461 {
1462   GSList *walk;
1463   gint64 interval;
1464
1465   interval = mix->master->queued;
1466   if (interval <= 0) {
1467     if (mix->fps_n == 0) {
1468       interval = G_MAXINT64;
1469     } else {
1470       interval = gst_util_uint64_scale_int (GST_SECOND, mix->fps_d, mix->fps_n);
1471     }
1472     GST_LOG_OBJECT (mix, "set interval to %" G_GINT64_FORMAT " nanoseconds",
1473         interval);
1474   }
1475
1476   walk = mix->sinkpads;
1477   while (walk) {
1478     GstVideoMixerPad *pad = GST_VIDEO_MIXER_PAD (walk->data);
1479     GstVideoMixerCollect *mixcol = pad->mixcol;
1480
1481     walk = g_slist_next (walk);
1482
1483     if (mixcol->buffer != NULL) {
1484       pad->queued -= interval;
1485       GST_LOG_OBJECT (pad, "queued now %" G_GINT64_FORMAT, pad->queued);
1486       if (pad->queued <= 0) {
1487         GstBuffer *buffer =
1488             gst_collect_pads_pop (mix->collect, &mixcol->collect);
1489
1490         GST_LOG_OBJECT (pad, "unreffing buffer");
1491         if (buffer)
1492           gst_buffer_unref (buffer);
1493         else
1494           GST_WARNING_OBJECT (pad,
1495               "Buffer was removed by GstCollectPads in the meantime");
1496
1497         gst_buffer_unref (mixcol->buffer);
1498         mixcol->buffer = NULL;
1499       }
1500     }
1501   }
1502 }
1503
1504 static GstFlowReturn
1505 gst_videomixer_collected (GstCollectPads * pads, GstVideoMixer * mix)
1506 {
1507   GstFlowReturn ret = GST_FLOW_OK;
1508   GstBuffer *outbuf = NULL;
1509   size_t outsize = 0;
1510   gboolean eos = FALSE;
1511   GstClockTime timestamp = GST_CLOCK_TIME_NONE;
1512   GstClockTime duration = GST_CLOCK_TIME_NONE;
1513
1514   g_return_val_if_fail (GST_IS_VIDEO_MIXER (mix), GST_FLOW_ERROR);
1515
1516   /* This must be set, otherwise we have no caps */
1517   if (G_UNLIKELY (mix->in_width == 0))
1518     return GST_FLOW_NOT_NEGOTIATED;
1519
1520   if (g_atomic_int_compare_and_exchange (&mix->flush_stop_pending, TRUE, FALSE)) {
1521     GST_DEBUG_OBJECT (mix, "pending flush stop");
1522     gst_pad_push_event (mix->srcpad, gst_event_new_flush_stop ());
1523   }
1524
1525   GST_LOG_OBJECT (mix, "all pads are collected");
1526   GST_VIDEO_MIXER_STATE_LOCK (mix);
1527
1528   eos = gst_videomixer_fill_queues (mix);
1529
1530   if (eos) {
1531     /* Push EOS downstream */
1532     GST_LOG_OBJECT (mix, "all our sinkpads are EOS, pushing downstream");
1533     gst_pad_push_event (mix->srcpad, gst_event_new_eos ());
1534     ret = GST_FLOW_WRONG_STATE;
1535     goto error;
1536   }
1537
1538   /* If geometry has changed we need to set new caps on the buffer */
1539   if (mix->in_width != mix->out_width || mix->in_height != mix->out_height
1540       || mix->setcaps) {
1541     GstCaps *newcaps = NULL;
1542
1543     newcaps = gst_caps_make_writable
1544         (gst_pad_get_negotiated_caps (GST_PAD (mix->master)));
1545     gst_caps_set_simple (newcaps,
1546         "width", G_TYPE_INT, mix->in_width,
1547         "height", G_TYPE_INT, mix->in_height,
1548         "pixel-aspect-ratio", GST_TYPE_FRACTION, mix->par_n, mix->par_d, NULL);
1549
1550     mix->out_width = mix->in_width;
1551     mix->out_height = mix->in_height;
1552     mix->setcaps = FALSE;
1553
1554     /* Calculating out buffer size from input size */
1555     gst_pad_set_caps (mix->srcpad, newcaps);
1556     gst_caps_unref (newcaps);
1557   }
1558
1559   /* Get timestamp & duration */
1560   if (mix->master->mixcol->buffer != NULL) {
1561     GstClockTime in_ts;
1562     GstSegment *seg;
1563     GstVideoMixerCollect *mixcol = mix->master->mixcol;
1564
1565     seg = &mixcol->collect.segment;
1566     in_ts = GST_BUFFER_TIMESTAMP (mixcol->buffer);
1567
1568     timestamp = gst_segment_to_running_time (seg, GST_FORMAT_TIME, in_ts);
1569     duration = GST_BUFFER_DURATION (mixcol->buffer);
1570
1571     mix->last_ts = timestamp;
1572     mix->last_duration = duration;
1573   } else {
1574     timestamp = mix->last_ts;
1575     duration = mix->last_duration;
1576   }
1577
1578   if (GST_CLOCK_TIME_IS_VALID (duration))
1579     mix->last_ts += duration;
1580
1581   if (!gst_videomixer_do_qos (mix, timestamp)) {
1582     gst_videomixer_update_queues (mix);
1583     GST_VIDEO_MIXER_STATE_UNLOCK (mix);
1584     ret = GST_FLOW_OK;
1585     goto beach;
1586   }
1587
1588   /* allocate an output buffer */
1589   outsize =
1590       gst_video_format_get_size (mix->fmt, mix->out_width, mix->out_height);
1591   ret =
1592       gst_pad_alloc_buffer_and_set_caps (mix->srcpad, GST_BUFFER_OFFSET_NONE,
1593       outsize, GST_PAD_CAPS (mix->srcpad), &outbuf);
1594
1595   /* This must be set at this point, otherwise we have no src caps */
1596   g_assert (mix->blend != NULL);
1597
1598   if (ret != GST_FLOW_OK) {
1599     goto error;
1600   }
1601
1602   GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
1603   GST_BUFFER_DURATION (outbuf) = duration;
1604
1605   switch (mix->background) {
1606     case VIDEO_MIXER_BACKGROUND_CHECKER:
1607       mix->fill_checker (GST_BUFFER_DATA (outbuf), mix->out_width,
1608           mix->out_height);
1609       break;
1610     case VIDEO_MIXER_BACKGROUND_BLACK:
1611       mix->fill_color (GST_BUFFER_DATA (outbuf), mix->out_width,
1612           mix->out_height, 16, 128, 128);
1613       break;
1614     case VIDEO_MIXER_BACKGROUND_WHITE:
1615       mix->fill_color (GST_BUFFER_DATA (outbuf), mix->out_width,
1616           mix->out_height, 240, 128, 128);
1617       break;
1618     case VIDEO_MIXER_BACKGROUND_TRANSPARENT:
1619       orc_memset (GST_BUFFER_DATA (outbuf), 0,
1620           gst_video_format_get_row_stride (mix->fmt, 0,
1621               mix->out_width) * mix->out_height);
1622       break;
1623   }
1624
1625   gst_videomixer_blend_buffers (mix, outbuf);
1626
1627   gst_videomixer_update_queues (mix);
1628   GST_VIDEO_MIXER_STATE_UNLOCK (mix);
1629
1630   ret = gst_pad_push (mix->srcpad, outbuf);
1631
1632 beach:
1633   return ret;
1634
1635   /* ERRORS */
1636 error:
1637   {
1638     if (outbuf)
1639       gst_buffer_unref (outbuf);
1640
1641     GST_VIDEO_MIXER_STATE_UNLOCK (mix);
1642     goto beach;
1643   }
1644 }
1645
1646 static gboolean
1647 forward_event_func (GstPad * pad, GValue * ret, GstEvent * event)
1648 {
1649   gst_event_ref (event);
1650   GST_LOG_OBJECT (pad, "About to send event %s", GST_EVENT_TYPE_NAME (event));
1651   if (!gst_pad_push_event (pad, event)) {
1652     g_value_set_boolean (ret, FALSE);
1653     GST_WARNING_OBJECT (pad, "Sending event  %p (%s) failed.",
1654         event, GST_EVENT_TYPE_NAME (event));
1655   } else {
1656     GST_LOG_OBJECT (pad, "Sent event  %p (%s).",
1657         event, GST_EVENT_TYPE_NAME (event));
1658   }
1659   gst_object_unref (pad);
1660   return TRUE;
1661 }
1662
1663 /* forwards the event to all sinkpads, takes ownership of the
1664  * event
1665  *
1666  * Returns: TRUE if the event could be forwarded on all
1667  * sinkpads.
1668  */
1669 static gboolean
1670 forward_event (GstVideoMixer * mix, GstEvent * event)
1671 {
1672   GstIterator *it;
1673   GValue vret = { 0 };
1674
1675   GST_LOG_OBJECT (mix, "Forwarding event %p (%s)", event,
1676       GST_EVENT_TYPE_NAME (event));
1677
1678   g_value_init (&vret, G_TYPE_BOOLEAN);
1679   g_value_set_boolean (&vret, TRUE);
1680   it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (mix));
1681   gst_iterator_fold (it, (GstIteratorFoldFunction) forward_event_func, &vret,
1682       event);
1683   gst_iterator_free (it);
1684   gst_event_unref (event);
1685
1686   return g_value_get_boolean (&vret);
1687 }
1688
1689 static gboolean
1690 gst_videomixer_src_event (GstPad * pad, GstEvent * event)
1691 {
1692   GstVideoMixer *mix = GST_VIDEO_MIXER (gst_pad_get_parent (pad));
1693   gboolean result;
1694
1695   switch (GST_EVENT_TYPE (event)) {
1696     case GST_EVENT_QOS:{
1697       GstClockTimeDiff diff;
1698       GstClockTime timestamp;
1699       gdouble proportion;
1700
1701       gst_event_parse_qos (event, &proportion, &diff, &timestamp);
1702
1703       gst_videomixer_update_qos (mix, proportion, diff, timestamp);
1704       gst_event_unref (event);
1705
1706       /* TODO: The QoS event should be transformed and send upstream */
1707       result = TRUE;
1708       break;
1709     }
1710     case GST_EVENT_SEEK:
1711     {
1712       GstSeekFlags flags;
1713       GstSeekType curtype;
1714       gint64 cur;
1715
1716       /* parse the seek parameters */
1717       gst_event_parse_seek (event, NULL, NULL, &flags, &curtype,
1718           &cur, NULL, NULL);
1719
1720       /* check if we are flushing */
1721       if (flags & GST_SEEK_FLAG_FLUSH) {
1722         /* make sure we accept nothing anymore and return WRONG_STATE */
1723         gst_collect_pads_set_flushing (mix->collect, TRUE);
1724
1725         /* flushing seek, start flush downstream, the flush will be done
1726          * when all pads received a FLUSH_STOP. */
1727         gst_pad_push_event (mix->srcpad, gst_event_new_flush_start ());
1728       }
1729
1730       /* now wait for the collected to be finished and mark a new
1731        * segment */
1732       GST_OBJECT_LOCK (mix->collect);
1733       if (curtype == GST_SEEK_TYPE_SET)
1734         mix->segment_position = cur;
1735       else
1736         mix->segment_position = 0;
1737       mix->sendseg = TRUE;
1738
1739       if (flags & GST_SEEK_FLAG_FLUSH) {
1740         gst_collect_pads_set_flushing (mix->collect, FALSE);
1741
1742         /* we can't send FLUSH_STOP here since upstream could start pushing data
1743          * after we unlock mix->collect.
1744          * We set flush_stop_pending to TRUE instead and send FLUSH_STOP after
1745          * forwarding the seek upstream or from gst_videomixer_collected,
1746          * whichever happens first.
1747          */
1748         mix->flush_stop_pending = TRUE;
1749       }
1750
1751       GST_OBJECT_UNLOCK (mix->collect);
1752       gst_videomixer_reset_qos (mix);
1753
1754       result = forward_event (mix, event);
1755
1756       if (g_atomic_int_compare_and_exchange (&mix->flush_stop_pending,
1757               TRUE, FALSE)) {
1758         GST_DEBUG_OBJECT (mix, "pending flush stop");
1759         gst_pad_push_event (mix->srcpad, gst_event_new_flush_stop ());
1760       }
1761
1762       break;
1763     }
1764     case GST_EVENT_NAVIGATION:
1765       /* navigation is rather pointless. */
1766       result = FALSE;
1767       break;
1768     default:
1769       /* just forward the rest for now */
1770       result = forward_event (mix, event);
1771       break;
1772   }
1773   gst_object_unref (mix);
1774
1775   return result;
1776 }
1777
1778 static gboolean
1779 gst_videomixer_sink_event (GstPad * pad, GstEvent * event)
1780 {
1781   GstVideoMixerPad *vpad = GST_VIDEO_MIXER_PAD (pad);
1782   GstVideoMixer *videomixer = GST_VIDEO_MIXER (gst_pad_get_parent (pad));
1783   gboolean ret;
1784
1785   GST_DEBUG_OBJECT (pad, "Got %s event on pad %s:%s",
1786       GST_EVENT_TYPE_NAME (event), GST_DEBUG_PAD_NAME (pad));
1787
1788   switch (GST_EVENT_TYPE (event)) {
1789     case GST_EVENT_FLUSH_STOP:
1790       /* mark a pending new segment. This event is synchronized
1791        * with the streaming thread so we can safely update the
1792        * variable without races. It's somewhat weird because we
1793        * assume the collectpads forwarded the FLUSH_STOP past us
1794        * and downstream (using our source pad, the bastard!).
1795        */
1796       videomixer->sendseg = TRUE;
1797       videomixer->flush_stop_pending = FALSE;
1798       gst_videomixer_reset_qos (videomixer);
1799
1800       /* Reset pad state after FLUSH_STOP */
1801       if (vpad->mixcol->buffer)
1802         gst_buffer_unref (vpad->mixcol->buffer);
1803       vpad->mixcol->buffer = NULL;
1804       vpad->queued = 0;
1805       break;
1806     case GST_EVENT_NEWSEGMENT:
1807       if (!videomixer->master || vpad == videomixer->master) {
1808         videomixer->sendseg = TRUE;
1809         gst_videomixer_reset_qos (videomixer);
1810       }
1811       break;
1812     default:
1813       break;
1814   }
1815
1816   /* now GstCollectPads can take care of the rest, e.g. EOS */
1817   ret = videomixer->collect_event (pad, event);
1818
1819   gst_object_unref (videomixer);
1820   return ret;
1821 }
1822
1823
1824 static void
1825 gst_videomixer_get_property (GObject * object,
1826     guint prop_id, GValue * value, GParamSpec * pspec)
1827 {
1828   GstVideoMixer *mix = GST_VIDEO_MIXER (object);
1829
1830   switch (prop_id) {
1831     case PROP_BACKGROUND:
1832       g_value_set_enum (value, mix->background);
1833       break;
1834     default:
1835       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1836       break;
1837   }
1838 }
1839
1840 static void
1841 gst_videomixer_set_property (GObject * object,
1842     guint prop_id, const GValue * value, GParamSpec * pspec)
1843 {
1844   GstVideoMixer *mix = GST_VIDEO_MIXER (object);
1845
1846   switch (prop_id) {
1847     case PROP_BACKGROUND:
1848       mix->background = g_value_get_enum (value);
1849       break;
1850     default:
1851       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1852       break;
1853   }
1854 }
1855
1856 static GstStateChangeReturn
1857 gst_videomixer_change_state (GstElement * element, GstStateChange transition)
1858 {
1859   GstVideoMixer *mix;
1860   GstStateChangeReturn ret;
1861
1862   g_return_val_if_fail (GST_IS_VIDEO_MIXER (element), GST_STATE_CHANGE_FAILURE);
1863
1864   mix = GST_VIDEO_MIXER (element);
1865
1866   switch (transition) {
1867     case GST_STATE_CHANGE_READY_TO_PAUSED:
1868       GST_LOG_OBJECT (mix, "starting collectpads");
1869       gst_collect_pads_start (mix->collect);
1870       break;
1871     case GST_STATE_CHANGE_PAUSED_TO_READY:
1872       GST_LOG_OBJECT (mix, "stopping collectpads");
1873       gst_collect_pads_stop (mix->collect);
1874       break;
1875     default:
1876       break;
1877   }
1878
1879   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1880
1881   switch (transition) {
1882     case GST_STATE_CHANGE_PAUSED_TO_READY:
1883       gst_videomixer_reset (mix);
1884       break;
1885     default:
1886       break;
1887   }
1888
1889   return ret;
1890 }
1891
1892 static gboolean
1893 plugin_init (GstPlugin * plugin)
1894 {
1895   GST_DEBUG_CATEGORY_INIT (gst_videomixer_debug, "videomixer", 0,
1896       "video mixer");
1897
1898   gst_video_mixer_init_blend ();
1899
1900   return gst_element_register (plugin, "videomixer", GST_RANK_PRIMARY,
1901       GST_TYPE_VIDEO_MIXER) && gst_videomixer2_register (plugin);
1902 }
1903
1904 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1905     GST_VERSION_MINOR,
1906     "videomixer",
1907     "Video mixer", plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME,
1908     GST_PACKAGE_ORIGIN)