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