f87f4b76171d947dbf12fb474fd7dc9426a69376
[platform/upstream/gstreamer.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., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 /**
22  * SECTION:element-videomixer
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  * Videomixer will do colorspace conversion.
30  * 
31  * Individual parameters for each input stream can be configured on the
32  * #GstVideoMixer2Pad.
33  *
34  * <refsect2>
35  * <title>Sample pipelines</title>
36  * |[
37  * gst-launch-1.0 \
38  *   videotestsrc pattern=1 ! \
39  *   video/x-raw,format=AYUV,framerate=\(fraction\)10/1,width=100,height=100 ! \
40  *   videobox border-alpha=0 top=-70 bottom=-70 right=-220 ! \
41  *   videomixer name=mix sink_0::alpha=0.7 sink_1::alpha=0.5 ! \
42  *   videoconvert ! xvimagesink \
43  *   videotestsrc ! \
44  *   video/x-raw,format=AYUV,framerate=\(fraction\)5/1,width=320,height=240 ! mix.
45  * ]| A pipeline to demonstrate videomixer used together with videobox.
46  * This should show a 320x240 pixels video test source with some transparency
47  * showing the background checker pattern. Another video test source with just
48  * the snow pattern of 100x100 pixels is overlayed on top of the first one on
49  * the left vertically centered with a small transparency showing the first
50  * video test source behind and the checker pattern under it. Note that the
51  * framerate of the output video is 10 frames per second.
52  * |[
53  * gst-launch-1.0 videotestsrc pattern=1 ! \
54  *   video/x-raw, framerate=\(fraction\)10/1, width=100, height=100 ! \
55  *   videomixer name=mix ! videoconvert ! ximagesink \
56  *   videotestsrc !  \
57  *   video/x-raw, framerate=\(fraction\)5/1, width=320, height=240 ! mix.
58  * ]| A pipeline to demostrate bgra mixing. (This does not demonstrate alpha blending). 
59  * |[
60  * gst-launch-1.0 videotestsrc pattern=1 ! \
61  *   video/x-raw,format =I420, framerate=\(fraction\)10/1, width=100, height=100 ! \
62  *   videomixer name=mix ! videoconvert ! ximagesink \
63  *   videotestsrc ! \
64  *   video/x-raw,format=I420, framerate=\(fraction\)5/1, width=320, height=240 ! mix.
65  * ]| A pipeline to test I420
66  * |[
67  * gst-launch-1.0 videomixer name=mixer sink_1::alpha=0.5 sink_1::xpos=50 sink_1::ypos=50 ! \
68  *   videoconvert ! ximagesink \
69  *   videotestsrc pattern=snow timestamp-offset=3000000000 ! \
70  *   "video/x-raw,format=AYUV,width=640,height=480,framerate=(fraction)30/1" ! \
71  *   timeoverlay ! queue2 ! mixer. \
72  *   videotestsrc pattern=smpte ! \
73  *   "video/x-raw,format=AYUV,width=800,height=600,framerate=(fraction)10/1" ! \
74  *   timeoverlay ! queue2 ! mixer.
75  * ]| A pipeline to demonstrate synchronized mixing (the second stream starts after 3 seconds)
76  * </refsect2>
77  */
78
79 #ifdef HAVE_CONFIG_H
80 #include "config.h"
81 #endif
82
83 #include <string.h>
84
85 #include "videomixer2.h"
86 #include "videomixer2pad.h"
87 #include "videoconvert.h"
88
89 #ifdef DISABLE_ORC
90 #define orc_memset memset
91 #else
92 #include <orc/orcfunctions.h>
93 #endif
94
95 GST_DEBUG_CATEGORY_STATIC (gst_videomixer2_debug);
96 #define GST_CAT_DEFAULT gst_videomixer2_debug
97
98 #define GST_VIDEO_MIXER2_GET_LOCK(mix) \
99   (&GST_VIDEO_MIXER2(mix)->lock)
100 #define GST_VIDEO_MIXER2_LOCK(mix) \
101   (g_mutex_lock(GST_VIDEO_MIXER2_GET_LOCK (mix)))
102 #define GST_VIDEO_MIXER2_UNLOCK(mix) \
103   (g_mutex_unlock(GST_VIDEO_MIXER2_GET_LOCK (mix)))
104 #define GST_VIDEO_MIXER2_GET_SETCAPS_LOCK(mix) \
105   (&GST_VIDEO_MIXER2(mix)->setcaps_lock)
106 #define GST_VIDEO_MIXER2_SETCAPS_LOCK(mix) \
107   (g_mutex_lock(GST_VIDEO_MIXER2_GET_SETCAPS_LOCK (mix)))
108 #define GST_VIDEO_MIXER2_SETCAPS_UNLOCK(mix) \
109   (g_mutex_unlock(GST_VIDEO_MIXER2_GET_SETCAPS_LOCK (mix)))
110
111 #define FORMATS " { AYUV, BGRA, ARGB, RGBA, ABGR, Y444, Y42B, YUY2, UYVY, "\
112                 "   YVYU, I420, YV12, NV12, NV21, Y41B, RGB, BGR, xRGB, xBGR, "\
113                 "   RGBx, BGRx } "
114
115 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
116     GST_PAD_SRC,
117     GST_PAD_ALWAYS,
118     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (FORMATS))
119     );
120
121 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%u",
122     GST_PAD_SINK,
123     GST_PAD_REQUEST,
124     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (FORMATS))
125     );
126
127 static void gst_videomixer2_child_proxy_init (gpointer g_iface,
128     gpointer iface_data);
129 static gboolean gst_videomixer2_push_sink_event (GstVideoMixer2 * mix,
130     GstEvent * event);
131 static void gst_videomixer2_release_pad (GstElement * element, GstPad * pad);
132 static void gst_videomixer2_reset_qos (GstVideoMixer2 * mix);
133
134 struct _GstVideoMixer2Collect
135 {
136   GstCollectData collect;       /* we extend the CollectData */
137
138   GstVideoMixer2Pad *mixpad;
139
140   GstBuffer *queued;            /* buffer for which we don't know the end time yet */
141   GstVideoInfo queued_vinfo;
142
143   GstBuffer *buffer;            /* buffer that should be blended now */
144   GstVideoInfo buffer_vinfo;
145
146   GstClockTime start_time;
147   GstClockTime end_time;
148 };
149
150 #define DEFAULT_PAD_ZORDER 0
151 #define DEFAULT_PAD_XPOS   0
152 #define DEFAULT_PAD_YPOS   0
153 #define DEFAULT_PAD_ALPHA  1.0
154 enum
155 {
156   PROP_PAD_0,
157   PROP_PAD_ZORDER,
158   PROP_PAD_XPOS,
159   PROP_PAD_YPOS,
160   PROP_PAD_ALPHA
161 };
162
163 G_DEFINE_TYPE (GstVideoMixer2Pad, gst_videomixer2_pad, GST_TYPE_PAD);
164
165 static void
166 gst_videomixer2_collect_free (GstCollectData * data)
167 {
168   GstVideoMixer2Collect *cdata = (GstVideoMixer2Collect *) data;
169
170   gst_buffer_replace (&cdata->buffer, NULL);
171 }
172
173 static gboolean gst_videomixer2_src_setcaps (GstPad * pad, GstVideoMixer2 * mix,
174     GstCaps * caps);
175
176 static gboolean
177 gst_videomixer2_update_src_caps (GstVideoMixer2 * mix)
178 {
179   GSList *l;
180   gint best_width = -1, best_height = -1;
181   gdouble best_fps = -1, cur_fps;
182   gint best_fps_n = -1, best_fps_d = -1;
183   gboolean ret = TRUE;
184
185   GST_VIDEO_MIXER2_SETCAPS_LOCK (mix);
186   GST_VIDEO_MIXER2_LOCK (mix);
187
188   for (l = mix->sinkpads; l; l = l->next) {
189     GstVideoMixer2Pad *mpad = l->data;
190     gint this_width, this_height;
191     gint fps_n, fps_d;
192     gint width, height;
193
194     fps_n = GST_VIDEO_INFO_FPS_N (&mpad->info);
195     fps_d = GST_VIDEO_INFO_FPS_D (&mpad->info);
196     width = GST_VIDEO_INFO_WIDTH (&mpad->info);
197     height = GST_VIDEO_INFO_HEIGHT (&mpad->info);
198
199     if (width == 0 || height == 0)
200       continue;
201
202     this_width = width + MAX (mpad->xpos, 0);
203     this_height = height + MAX (mpad->ypos, 0);
204
205     if (best_width < this_width)
206       best_width = this_width;
207     if (best_height < this_height)
208       best_height = this_height;
209
210     if (fps_d == 0)
211       cur_fps = 0.0;
212     else
213       gst_util_fraction_to_double (fps_n, fps_d, &cur_fps);
214
215     if (best_fps < cur_fps) {
216       best_fps = cur_fps;
217       best_fps_n = fps_n;
218       best_fps_d = fps_d;
219     }
220   }
221
222   if (best_fps_n <= 0 || best_fps_d <= 0 || best_fps == 0.0) {
223     best_fps_n = 25;
224     best_fps_d = 1;
225     best_fps = 25.0;
226   }
227
228   if (best_width > 0 && best_height > 0 && best_fps > 0) {
229     GstCaps *caps, *peercaps;
230     GstStructure *s;
231     GstVideoInfo info;
232
233     if (GST_VIDEO_INFO_FPS_N (&mix->info) != best_fps_n ||
234         GST_VIDEO_INFO_FPS_D (&mix->info) != best_fps_d) {
235       if (mix->segment.position != -1) {
236         mix->ts_offset = mix->segment.position - mix->segment.start;
237         mix->nframes = 0;
238       }
239     }
240     gst_video_info_init (&info);
241     gst_video_info_set_format (&info, GST_VIDEO_INFO_FORMAT (&mix->info),
242         best_width, best_height);
243     info.fps_n = best_fps_n;
244     info.fps_d = best_fps_d;
245     info.par_n = GST_VIDEO_INFO_PAR_N (&mix->info);
246     info.par_d = GST_VIDEO_INFO_PAR_D (&mix->info);
247
248     caps = gst_video_info_to_caps (&info);
249
250     peercaps = gst_pad_peer_query_caps (mix->srcpad, NULL);
251     if (peercaps) {
252       GstCaps *tmp;
253
254       s = gst_caps_get_structure (caps, 0);
255       gst_structure_set (s, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT, "height",
256           GST_TYPE_INT_RANGE, 1, G_MAXINT, "framerate", GST_TYPE_FRACTION_RANGE,
257           0, 1, G_MAXINT, 1, NULL);
258
259       tmp = gst_caps_intersect (caps, peercaps);
260       gst_caps_unref (caps);
261       gst_caps_unref (peercaps);
262       caps = tmp;
263       if (gst_caps_is_empty (caps)) {
264         GST_DEBUG_OBJECT (mix, "empty caps");
265         ret = FALSE;
266         GST_VIDEO_MIXER2_UNLOCK (mix);
267         goto done;
268       }
269
270       caps = gst_caps_truncate (caps);
271       s = gst_caps_get_structure (caps, 0);
272       gst_structure_fixate_field_nearest_int (s, "width", best_width);
273       gst_structure_fixate_field_nearest_int (s, "height", best_height);
274       gst_structure_fixate_field_nearest_fraction (s, "framerate", best_fps_n,
275           best_fps_d);
276
277       gst_structure_get_int (s, "width", &info.width);
278       gst_structure_get_int (s, "height", &info.height);
279       gst_structure_get_fraction (s, "fraction", &info.fps_n, &info.fps_d);
280     }
281
282     gst_caps_unref (caps);
283     caps = gst_video_info_to_caps (&info);
284
285     GST_VIDEO_MIXER2_UNLOCK (mix);
286     ret = gst_videomixer2_src_setcaps (mix->srcpad, mix, caps);
287     gst_caps_unref (caps);
288   } else {
289     GST_VIDEO_MIXER2_UNLOCK (mix);
290   }
291
292 done:
293   GST_VIDEO_MIXER2_SETCAPS_UNLOCK (mix);
294
295   return ret;
296 }
297
298 static gboolean
299 gst_videomixer2_update_converters (GstVideoMixer2 * mix)
300 {
301   GSList *tmp;
302   GstVideoFormat best_format;
303   GstVideoInfo best_info;
304   GstVideoMixer2Pad *pad;
305   gboolean need_alpha = FALSE;
306   gboolean at_least_one_alpha = FALSE;
307   GstCaps *downstream_caps;
308   GstCaps *possible_caps;
309   gchar *best_colorimetry;
310   const gchar *best_chroma;
311   GHashTable *formats_table;
312   gint best_format_number = 0;
313
314   best_format = GST_VIDEO_FORMAT_UNKNOWN;
315   gst_video_info_init (&best_info);
316
317   downstream_caps = gst_pad_get_allowed_caps (mix->srcpad);
318
319   if (!downstream_caps || gst_caps_is_empty (downstream_caps))
320     return FALSE;
321
322   formats_table = g_hash_table_new (g_direct_hash, g_direct_equal);
323
324   /* first find new preferred format */
325   for (tmp = mix->sinkpads; tmp; tmp = tmp->next) {
326     GstStructure *s;
327     gint format_number;
328
329     pad = tmp->data;
330
331     if (!pad->info.finfo)
332       continue;
333
334     if (pad->info.finfo->flags & GST_VIDEO_FORMAT_FLAG_ALPHA)
335       at_least_one_alpha = TRUE;
336
337     /* If we want alpha, disregard all the other formats */
338     if (need_alpha && !(pad->info.finfo->flags & GST_VIDEO_FORMAT_FLAG_ALPHA))
339       continue;
340
341     /* This can happen if we release a pad and another pad hasn't been negotiated yet */
342     if (GST_VIDEO_INFO_FORMAT (&pad->info) == GST_VIDEO_FORMAT_UNKNOWN)
343       continue;
344
345     possible_caps = gst_video_info_to_caps (&pad->info);
346
347     s = gst_caps_get_structure (possible_caps, 0);
348     gst_structure_remove_fields (s, "width", "height", "framerate",
349         "pixel-aspect-ratio", "interlace-mode", NULL);
350
351     /* Can downstream accept this format ? */
352     if (!gst_caps_can_intersect (downstream_caps, possible_caps)) {
353       gst_caps_unref (possible_caps);
354       continue;
355     }
356
357     gst_caps_unref (possible_caps);
358
359     format_number =
360         GPOINTER_TO_INT (g_hash_table_lookup (formats_table,
361             GINT_TO_POINTER (GST_VIDEO_INFO_FORMAT (&pad->info))));
362     format_number += 1;
363
364     g_hash_table_replace (formats_table,
365         GINT_TO_POINTER (GST_VIDEO_INFO_FORMAT (&pad->info)),
366         GINT_TO_POINTER (format_number));
367
368     /* If that pad is the first with alpha, set it as the new best format */
369     if (!need_alpha && (pad->info.finfo->flags & GST_VIDEO_FORMAT_FLAG_ALPHA)) {
370       need_alpha = TRUE;
371       best_format = GST_VIDEO_INFO_FORMAT (&pad->info);
372       best_info = pad->info;
373       best_format_number = format_number;
374     } else if (format_number > best_format_number) {
375       best_format = GST_VIDEO_INFO_FORMAT (&pad->info);
376       best_info = pad->info;
377       best_format_number = format_number;
378     }
379   }
380
381   g_hash_table_unref (formats_table);
382
383   if (best_format == GST_VIDEO_FORMAT_UNKNOWN) {
384     downstream_caps = gst_caps_fixate (downstream_caps);
385     gst_video_info_from_caps (&best_info, downstream_caps);
386     best_format = GST_VIDEO_INFO_FORMAT (&best_info);
387   }
388
389   gst_caps_unref (downstream_caps);
390
391   if (at_least_one_alpha
392       && !(best_info.finfo->flags & GST_VIDEO_FORMAT_FLAG_ALPHA)) {
393     GST_ELEMENT_ERROR (mix, CORE, NEGOTIATION,
394         ("At least one of the input pads contains alpha, but downstream can't support alpha."),
395         ("Either convert your inputs to not contain alpha or add a videoconvert after the mixer"));
396     return FALSE;
397   }
398
399   best_colorimetry = gst_video_colorimetry_to_string (&(best_info.colorimetry));
400   best_chroma = gst_video_chroma_to_string (best_info.chroma_site);
401   mix->info = best_info;
402
403   GST_DEBUG_OBJECT (mix,
404       "The output format will now be : %d with colorimetry : %s and chroma : %s",
405       best_format, best_colorimetry, best_chroma);
406
407   /* Then browse the sinks once more, setting or unsetting conversion if needed */
408   for (tmp = mix->sinkpads; tmp; tmp = tmp->next) {
409     gchar *colorimetry;
410     const gchar *chroma;
411
412     pad = tmp->data;
413
414     if (!pad->info.finfo)
415       continue;
416
417     if (GST_VIDEO_INFO_FORMAT (&pad->info) == GST_VIDEO_FORMAT_UNKNOWN)
418       continue;
419
420     if (pad->convert)
421       videomixer_videoconvert_convert_free (pad->convert);
422
423     pad->convert = NULL;
424
425     colorimetry = gst_video_colorimetry_to_string (&(pad->info.colorimetry));
426     chroma = gst_video_chroma_to_string (pad->info.chroma_site);
427
428     if (best_format != GST_VIDEO_INFO_FORMAT (&pad->info) ||
429         g_strcmp0 (colorimetry, best_colorimetry) ||
430         g_strcmp0 (chroma, best_chroma)) {
431       GST_DEBUG_OBJECT (pad, "This pad will be converted from %d to %d",
432           GST_VIDEO_INFO_FORMAT (&pad->info),
433           GST_VIDEO_INFO_FORMAT (&best_info));
434       pad->convert =
435           videomixer_videoconvert_convert_new (&pad->info, &best_info);
436       pad->need_conversion_update = TRUE;
437       if (!pad->convert) {
438         g_free (colorimetry);
439         g_free (best_colorimetry);
440         GST_WARNING ("No path found for conversion");
441         return FALSE;
442       }
443     } else {
444       GST_DEBUG_OBJECT (pad, "This pad will not need conversion");
445     }
446     g_free (colorimetry);
447   }
448
449   g_free (best_colorimetry);
450   return TRUE;
451 }
452
453 static gboolean
454 gst_videomixer2_pad_sink_setcaps (GstPad * pad, GstObject * parent,
455     GstCaps * caps)
456 {
457   GstVideoMixer2 *mix;
458   GstVideoMixer2Pad *mixpad;
459   GstVideoInfo info;
460   gboolean ret = FALSE;
461
462   GST_INFO_OBJECT (pad, "Setting caps %" GST_PTR_FORMAT, caps);
463
464   mix = GST_VIDEO_MIXER2 (parent);
465   mixpad = GST_VIDEO_MIXER2_PAD (pad);
466
467   if (!gst_video_info_from_caps (&info, caps)) {
468     GST_ERROR_OBJECT (pad, "Failed to parse caps");
469     goto beach;
470   }
471
472   GST_VIDEO_MIXER2_LOCK (mix);
473   if (GST_VIDEO_INFO_FORMAT (&mix->info) != GST_VIDEO_FORMAT_UNKNOWN) {
474     if (GST_VIDEO_INFO_PAR_N (&mix->info) != GST_VIDEO_INFO_PAR_N (&info)
475         || GST_VIDEO_INFO_PAR_D (&mix->info) != GST_VIDEO_INFO_PAR_D (&info) ||
476         GST_VIDEO_INFO_INTERLACE_MODE (&mix->info) !=
477         GST_VIDEO_INFO_INTERLACE_MODE (&info)) {
478       GST_DEBUG_OBJECT (pad,
479           "got input caps %" GST_PTR_FORMAT ", but " "current caps are %"
480           GST_PTR_FORMAT, caps, mix->current_caps);
481       GST_VIDEO_MIXER2_UNLOCK (mix);
482       return FALSE;
483     }
484   }
485
486   mixpad->info = info;
487
488   GST_COLLECT_PADS_STREAM_LOCK (mix->collect);
489
490   ret = gst_videomixer2_update_converters (mix);
491
492   GST_VIDEO_MIXER2_UNLOCK (mix);
493   if (ret)
494     ret = gst_videomixer2_update_src_caps (mix);
495   GST_COLLECT_PADS_STREAM_UNLOCK (mix->collect);
496
497 beach:
498   return ret;
499 }
500
501 static GstCaps *
502 gst_videomixer2_pad_sink_getcaps (GstPad * pad, GstVideoMixer2 * mix,
503     GstCaps * filter)
504 {
505   GstCaps *srccaps;
506   GstCaps *template_caps;
507   GstCaps *filtered_caps;
508   GstCaps *returned_caps;
509   GstStructure *s;
510   gboolean had_current_caps = TRUE;
511   gint i, n;
512
513   template_caps = gst_pad_get_pad_template_caps (GST_PAD (mix->srcpad));
514
515   srccaps = gst_pad_get_current_caps (GST_PAD (mix->srcpad));
516   if (srccaps == NULL) {
517     had_current_caps = FALSE;
518     srccaps = template_caps;
519   }
520
521   srccaps = gst_caps_make_writable (srccaps);
522
523   n = gst_caps_get_size (srccaps);
524   for (i = 0; i < n; i++) {
525     s = gst_caps_get_structure (srccaps, i);
526     gst_structure_set (s, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
527         "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
528         "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
529     if (!gst_structure_has_field (s, "pixel-aspect-ratio"))
530       gst_structure_set (s, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
531           NULL);
532
533     gst_structure_remove_fields (s, "colorimetry", "chroma-site", "format",
534         NULL);
535   }
536
537   filtered_caps = srccaps;
538   if (filter)
539     filtered_caps = gst_caps_intersect (srccaps, filter);
540   returned_caps = gst_caps_intersect (filtered_caps, template_caps);
541
542   gst_caps_unref (srccaps);
543   if (filter)
544     gst_caps_unref (filtered_caps);
545   if (had_current_caps)
546     gst_caps_unref (template_caps);
547
548   return returned_caps;
549 }
550
551 static gboolean
552 gst_videomixer2_pad_sink_acceptcaps (GstPad * pad, GstVideoMixer2 * mix,
553     GstCaps * caps)
554 {
555   gboolean ret;
556   GstCaps *modified_caps;
557   GstCaps *accepted_caps;
558   GstCaps *template_caps;
559   gboolean had_current_caps = TRUE;
560   gint i, n;
561   GstStructure *s;
562
563   GST_DEBUG_OBJECT (pad, "%" GST_PTR_FORMAT, caps);
564
565   accepted_caps = gst_pad_get_current_caps (GST_PAD (mix->srcpad));
566
567   template_caps = gst_pad_get_pad_template_caps (GST_PAD (mix->srcpad));
568
569   if (accepted_caps == NULL) {
570     accepted_caps = template_caps;
571     had_current_caps = FALSE;
572   }
573
574   accepted_caps = gst_caps_make_writable (accepted_caps);
575
576   GST_LOG_OBJECT (pad, "src caps %" GST_PTR_FORMAT, accepted_caps);
577
578   n = gst_caps_get_size (accepted_caps);
579   for (i = 0; i < n; i++) {
580     s = gst_caps_get_structure (accepted_caps, i);
581     gst_structure_set (s, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
582         "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
583         "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
584     if (!gst_structure_has_field (s, "pixel-aspect-ratio"))
585       gst_structure_set (s, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
586           NULL);
587
588     gst_structure_remove_fields (s, "colorimetry", "chroma-site", "format",
589         NULL);
590   }
591
592   modified_caps = gst_caps_intersect (accepted_caps, template_caps);
593
594   ret = gst_caps_can_intersect (caps, accepted_caps);
595   GST_DEBUG_OBJECT (pad, "%saccepted caps %" GST_PTR_FORMAT,
596       (ret ? "" : "not "), caps);
597   GST_DEBUG_OBJECT (pad, "acceptable caps are %" GST_PTR_FORMAT, accepted_caps);
598   gst_caps_unref (accepted_caps);
599   gst_caps_unref (modified_caps);
600   if (had_current_caps)
601     gst_caps_unref (template_caps);
602   return ret;
603 }
604
605 static gboolean
606 gst_videomixer2_sink_query (GstCollectPads * pads, GstCollectData * cdata,
607     GstQuery * query, GstVideoMixer2 * mix)
608 {
609   GstVideoMixer2Pad *pad = GST_VIDEO_MIXER2_PAD (cdata->pad);
610   gboolean ret = FALSE;
611
612   switch (GST_QUERY_TYPE (query)) {
613     case GST_QUERY_CAPS:
614     {
615       GstCaps *filter, *caps;
616
617       gst_query_parse_caps (query, &filter);
618       caps = gst_videomixer2_pad_sink_getcaps (GST_PAD (pad), mix, filter);
619       gst_query_set_caps_result (query, caps);
620       gst_caps_unref (caps);
621       ret = TRUE;
622       break;
623     }
624     case GST_QUERY_ACCEPT_CAPS:
625     {
626       GstCaps *caps;
627
628       gst_query_parse_accept_caps (query, &caps);
629       ret = gst_videomixer2_pad_sink_acceptcaps (GST_PAD (pad), mix, caps);
630       gst_query_set_accept_caps_result (query, ret);
631       ret = TRUE;
632       break;
633     }
634     default:
635       ret = gst_collect_pads_query_default (pads, cdata, query, FALSE);
636       break;
637   }
638   return ret;
639 }
640
641 static void
642 gst_videomixer2_pad_get_property (GObject * object, guint prop_id,
643     GValue * value, GParamSpec * pspec)
644 {
645   GstVideoMixer2Pad *pad = GST_VIDEO_MIXER2_PAD (object);
646
647   switch (prop_id) {
648     case PROP_PAD_ZORDER:
649       g_value_set_uint (value, pad->zorder);
650       break;
651     case PROP_PAD_XPOS:
652       g_value_set_int (value, pad->xpos);
653       break;
654     case PROP_PAD_YPOS:
655       g_value_set_int (value, pad->ypos);
656       break;
657     case PROP_PAD_ALPHA:
658       g_value_set_double (value, pad->alpha);
659       break;
660     default:
661       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
662       break;
663   }
664 }
665
666 static int
667 pad_zorder_compare (const GstVideoMixer2Pad * pad1,
668     const GstVideoMixer2Pad * pad2)
669 {
670   return pad1->zorder - pad2->zorder;
671 }
672
673 static void
674 gst_videomixer2_pad_set_property (GObject * object, guint prop_id,
675     const GValue * value, GParamSpec * pspec)
676 {
677   GstVideoMixer2Pad *pad = GST_VIDEO_MIXER2_PAD (object);
678   GstVideoMixer2 *mix = GST_VIDEO_MIXER2 (gst_pad_get_parent (GST_PAD (pad)));
679
680   switch (prop_id) {
681     case PROP_PAD_ZORDER:
682       GST_VIDEO_MIXER2_LOCK (mix);
683       pad->zorder = g_value_get_uint (value);
684
685       mix->sinkpads = g_slist_sort (mix->sinkpads,
686           (GCompareFunc) pad_zorder_compare);
687       GST_VIDEO_MIXER2_UNLOCK (mix);
688       break;
689     case PROP_PAD_XPOS:
690       pad->xpos = g_value_get_int (value);
691       break;
692     case PROP_PAD_YPOS:
693       pad->ypos = g_value_get_int (value);
694       break;
695     case PROP_PAD_ALPHA:
696       pad->alpha = g_value_get_double (value);
697       break;
698     default:
699       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
700       break;
701   }
702
703   gst_object_unref (mix);
704 }
705
706 static void
707 gst_videomixer2_pad_class_init (GstVideoMixer2PadClass * klass)
708 {
709   GObjectClass *gobject_class = (GObjectClass *) klass;
710
711   gobject_class->set_property = gst_videomixer2_pad_set_property;
712   gobject_class->get_property = gst_videomixer2_pad_get_property;
713
714   g_object_class_install_property (gobject_class, PROP_PAD_ZORDER,
715       g_param_spec_uint ("zorder", "Z-Order", "Z Order of the picture",
716           0, 10000, DEFAULT_PAD_ZORDER,
717           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
718   g_object_class_install_property (gobject_class, PROP_PAD_XPOS,
719       g_param_spec_int ("xpos", "X Position", "X Position of the picture",
720           G_MININT, G_MAXINT, DEFAULT_PAD_XPOS,
721           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
722   g_object_class_install_property (gobject_class, PROP_PAD_YPOS,
723       g_param_spec_int ("ypos", "Y Position", "Y Position of the picture",
724           G_MININT, G_MAXINT, DEFAULT_PAD_YPOS,
725           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
726   g_object_class_install_property (gobject_class, PROP_PAD_ALPHA,
727       g_param_spec_double ("alpha", "Alpha", "Alpha of the picture", 0.0, 1.0,
728           DEFAULT_PAD_ALPHA,
729           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
730 }
731
732 static void
733 gst_videomixer2_pad_init (GstVideoMixer2Pad * mixerpad)
734 {
735   mixerpad->zorder = DEFAULT_PAD_ZORDER;
736   mixerpad->xpos = DEFAULT_PAD_XPOS;
737   mixerpad->ypos = DEFAULT_PAD_YPOS;
738   mixerpad->alpha = DEFAULT_PAD_ALPHA;
739   mixerpad->convert = NULL;
740   mixerpad->need_conversion_update = FALSE;
741 }
742
743 /* GstVideoMixer2 */
744 #define DEFAULT_BACKGROUND VIDEO_MIXER2_BACKGROUND_CHECKER
745 enum
746 {
747   PROP_0,
748   PROP_BACKGROUND
749 };
750
751 #define GST_TYPE_VIDEO_MIXER2_BACKGROUND (gst_videomixer2_background_get_type())
752 static GType
753 gst_videomixer2_background_get_type (void)
754 {
755   static GType video_mixer_background_type = 0;
756
757   static const GEnumValue video_mixer_background[] = {
758     {VIDEO_MIXER2_BACKGROUND_CHECKER, "Checker pattern", "checker"},
759     {VIDEO_MIXER2_BACKGROUND_BLACK, "Black", "black"},
760     {VIDEO_MIXER2_BACKGROUND_WHITE, "White", "white"},
761     {VIDEO_MIXER2_BACKGROUND_TRANSPARENT,
762         "Transparent Background to enable further mixing", "transparent"},
763     {0, NULL, NULL},
764   };
765
766   if (!video_mixer_background_type) {
767     video_mixer_background_type =
768         g_enum_register_static ("GstVideoMixer2Background",
769         video_mixer_background);
770   }
771   return video_mixer_background_type;
772 }
773
774 #define gst_videomixer2_parent_class parent_class
775 G_DEFINE_TYPE_WITH_CODE (GstVideoMixer2, gst_videomixer2, GST_TYPE_ELEMENT,
776     G_IMPLEMENT_INTERFACE (GST_TYPE_CHILD_PROXY,
777         gst_videomixer2_child_proxy_init));
778
779 static void
780 gst_videomixer2_update_qos (GstVideoMixer2 * mix, gdouble proportion,
781     GstClockTimeDiff diff, GstClockTime timestamp)
782 {
783   GST_DEBUG_OBJECT (mix,
784       "Updating QoS: proportion %lf, diff %s%" GST_TIME_FORMAT ", timestamp %"
785       GST_TIME_FORMAT, proportion, (diff < 0) ? "-" : "",
786       GST_TIME_ARGS (ABS (diff)), GST_TIME_ARGS (timestamp));
787
788   GST_OBJECT_LOCK (mix);
789   mix->proportion = proportion;
790   if (G_LIKELY (timestamp != GST_CLOCK_TIME_NONE)) {
791     if (G_UNLIKELY (diff > 0))
792       mix->earliest_time =
793           timestamp + 2 * diff + gst_util_uint64_scale_int_round (GST_SECOND,
794           GST_VIDEO_INFO_FPS_D (&mix->info), GST_VIDEO_INFO_FPS_N (&mix->info));
795     else
796       mix->earliest_time = timestamp + diff;
797   } else {
798     mix->earliest_time = GST_CLOCK_TIME_NONE;
799   }
800   GST_OBJECT_UNLOCK (mix);
801 }
802
803 static void
804 gst_videomixer2_reset_qos (GstVideoMixer2 * mix)
805 {
806   gst_videomixer2_update_qos (mix, 0.5, 0, GST_CLOCK_TIME_NONE);
807   mix->qos_processed = mix->qos_dropped = 0;
808 }
809
810 static void
811 gst_videomixer2_read_qos (GstVideoMixer2 * mix, gdouble * proportion,
812     GstClockTime * time)
813 {
814   GST_OBJECT_LOCK (mix);
815   *proportion = mix->proportion;
816   *time = mix->earliest_time;
817   GST_OBJECT_UNLOCK (mix);
818 }
819
820 static void
821 gst_videomixer2_reset (GstVideoMixer2 * mix)
822 {
823   GSList *l;
824
825   gst_video_info_init (&mix->info);
826   mix->ts_offset = 0;
827   mix->nframes = 0;
828
829   gst_segment_init (&mix->segment, GST_FORMAT_TIME);
830   mix->segment.position = -1;
831
832   gst_videomixer2_reset_qos (mix);
833
834   for (l = mix->sinkpads; l; l = l->next) {
835     GstVideoMixer2Pad *p = l->data;
836     GstVideoMixer2Collect *mixcol = p->mixcol;
837
838     gst_buffer_replace (&mixcol->buffer, NULL);
839     mixcol->start_time = -1;
840     mixcol->end_time = -1;
841
842     gst_video_info_init (&p->info);
843   }
844
845   mix->newseg_pending = TRUE;
846 }
847
848 /*  1 == OK
849  *  0 == need more data
850  * -1 == EOS
851  * -2 == error
852  */
853 static gint
854 gst_videomixer2_fill_queues (GstVideoMixer2 * mix,
855     GstClockTime output_start_time, GstClockTime output_end_time)
856 {
857   GSList *l;
858   gboolean eos = TRUE;
859   gboolean need_more_data = FALSE;
860
861   for (l = mix->sinkpads; l; l = l->next) {
862     GstVideoMixer2Pad *pad = l->data;
863     GstVideoMixer2Collect *mixcol = pad->mixcol;
864     GstSegment *segment = &pad->mixcol->collect.segment;
865     GstBuffer *buf;
866     GstVideoInfo *vinfo;
867
868     buf = gst_collect_pads_peek (mix->collect, &mixcol->collect);
869     if (buf) {
870       GstClockTime start_time, end_time;
871
872       start_time = GST_BUFFER_TIMESTAMP (buf);
873       if (start_time == -1) {
874         gst_buffer_unref (buf);
875         GST_ERROR_OBJECT (pad, "Need timestamped buffers!");
876         return -2;
877       }
878
879       vinfo = &pad->info;
880
881       /* FIXME: Make all this work with negative rates */
882
883       if ((mixcol->buffer && start_time < GST_BUFFER_TIMESTAMP (mixcol->buffer))
884           || (mixcol->queued
885               && start_time < GST_BUFFER_TIMESTAMP (mixcol->queued))) {
886         GST_WARNING_OBJECT (pad, "Buffer from the past, dropping");
887         gst_buffer_unref (buf);
888         buf = gst_collect_pads_pop (mix->collect, &mixcol->collect);
889         gst_buffer_unref (buf);
890         need_more_data = TRUE;
891         continue;
892       }
893
894       if (mixcol->queued) {
895         end_time = start_time - GST_BUFFER_TIMESTAMP (mixcol->queued);
896         start_time = GST_BUFFER_TIMESTAMP (mixcol->queued);
897         gst_buffer_unref (buf);
898         buf = gst_buffer_ref (mixcol->queued);
899         vinfo = &mixcol->queued_vinfo;
900       } else {
901         end_time = GST_BUFFER_DURATION (buf);
902
903         if (end_time == -1) {
904           mixcol->queued = buf;
905           buf = gst_collect_pads_pop (mix->collect, &mixcol->collect);
906           gst_buffer_unref (buf);
907           mixcol->queued_vinfo = pad->info;
908           need_more_data = TRUE;
909           continue;
910         }
911       }
912
913       g_assert (start_time != -1 && end_time != -1);
914       end_time += start_time;   /* convert from duration to position */
915
916       /* Check if it's inside the segment */
917       if (start_time >= segment->stop || end_time < segment->start) {
918         GST_DEBUG_OBJECT (pad, "Buffer outside the segment");
919
920         if (buf == mixcol->queued) {
921           gst_buffer_unref (buf);
922           gst_buffer_replace (&mixcol->queued, NULL);
923         } else {
924           gst_buffer_unref (buf);
925           buf = gst_collect_pads_pop (mix->collect, &mixcol->collect);
926           gst_buffer_unref (buf);
927         }
928
929         need_more_data = TRUE;
930         continue;
931       }
932
933       /* Clip to segment and convert to running time */
934       start_time = MAX (start_time, segment->start);
935       if (segment->stop != -1)
936         end_time = MIN (end_time, segment->stop);
937       start_time =
938           gst_segment_to_running_time (segment, GST_FORMAT_TIME, start_time);
939       end_time =
940           gst_segment_to_running_time (segment, GST_FORMAT_TIME, end_time);
941       g_assert (start_time != -1 && end_time != -1);
942
943       /* Convert to the output segment rate */
944       if (ABS (mix->segment.rate) != 1.0) {
945         start_time *= ABS (mix->segment.rate);
946         end_time *= ABS (mix->segment.rate);
947       }
948
949       if (mixcol->end_time != -1 && mixcol->end_time > end_time) {
950         GST_DEBUG_OBJECT (pad, "Buffer from the past, dropping");
951         if (buf == mixcol->queued) {
952           gst_buffer_unref (buf);
953           gst_buffer_replace (&mixcol->queued, NULL);
954         } else {
955           gst_buffer_unref (buf);
956           buf = gst_collect_pads_pop (mix->collect, &mixcol->collect);
957           gst_buffer_unref (buf);
958         }
959
960         need_more_data = TRUE;
961         continue;
962       }
963
964       if (end_time >= output_start_time && start_time < output_end_time) {
965         GST_DEBUG_OBJECT (pad,
966             "Taking new buffer with start time %" GST_TIME_FORMAT,
967             GST_TIME_ARGS (start_time));
968         gst_buffer_replace (&mixcol->buffer, buf);
969         mixcol->buffer_vinfo = *vinfo;
970         mixcol->start_time = start_time;
971         mixcol->end_time = end_time;
972
973         if (buf == mixcol->queued) {
974           gst_buffer_unref (buf);
975           gst_buffer_replace (&mixcol->queued, NULL);
976         } else {
977           gst_buffer_unref (buf);
978           buf = gst_collect_pads_pop (mix->collect, &mixcol->collect);
979           gst_buffer_unref (buf);
980         }
981         eos = FALSE;
982       } else if (start_time >= output_end_time) {
983         GST_DEBUG_OBJECT (pad, "Keeping buffer until %" GST_TIME_FORMAT,
984             GST_TIME_ARGS (start_time));
985         gst_buffer_unref (buf);
986         eos = FALSE;
987       } else {
988         GST_DEBUG_OBJECT (pad, "Too old buffer -- dropping");
989         if (buf == mixcol->queued) {
990           gst_buffer_unref (buf);
991           gst_buffer_replace (&mixcol->queued, NULL);
992         } else {
993           gst_buffer_unref (buf);
994           buf = gst_collect_pads_pop (mix->collect, &mixcol->collect);
995           gst_buffer_unref (buf);
996         }
997
998         need_more_data = TRUE;
999         continue;
1000       }
1001     } else {
1002       if (mixcol->end_time != -1) {
1003         if (mixcol->end_time <= output_start_time) {
1004           gst_buffer_replace (&mixcol->buffer, NULL);
1005           mixcol->start_time = mixcol->end_time = -1;
1006           if (!GST_COLLECT_PADS_STATE_IS_SET (mixcol,
1007                   GST_COLLECT_PADS_STATE_EOS))
1008             need_more_data = TRUE;
1009         } else if (!GST_COLLECT_PADS_STATE_IS_SET (mixcol,
1010                 GST_COLLECT_PADS_STATE_EOS)) {
1011           eos = FALSE;
1012         }
1013       }
1014     }
1015   }
1016
1017   if (need_more_data)
1018     return 0;
1019   if (eos)
1020     return -1;
1021
1022   return 1;
1023 }
1024
1025 static GstFlowReturn
1026 gst_videomixer2_blend_buffers (GstVideoMixer2 * mix,
1027     GstClockTime output_start_time, GstClockTime output_end_time,
1028     GstBuffer ** outbuf)
1029 {
1030   GSList *l;
1031   guint outsize;
1032   BlendFunction composite;
1033   GstVideoFrame outframe;
1034   static GstAllocationParams params = { 0, 15, 0, 0, };
1035
1036   outsize = GST_VIDEO_INFO_SIZE (&mix->info);
1037
1038   *outbuf = gst_buffer_new_allocate (NULL, outsize, &params);
1039   GST_BUFFER_TIMESTAMP (*outbuf) = output_start_time;
1040   GST_BUFFER_DURATION (*outbuf) = output_end_time - output_start_time;
1041
1042   gst_video_frame_map (&outframe, &mix->info, *outbuf, GST_MAP_READWRITE);
1043
1044   /* default to blending */
1045   composite = mix->blend;
1046   switch (mix->background) {
1047     case VIDEO_MIXER2_BACKGROUND_CHECKER:
1048       mix->fill_checker (&outframe);
1049       break;
1050     case VIDEO_MIXER2_BACKGROUND_BLACK:
1051       mix->fill_color (&outframe, 16, 128, 128);
1052       break;
1053     case VIDEO_MIXER2_BACKGROUND_WHITE:
1054       mix->fill_color (&outframe, 240, 128, 128);
1055       break;
1056     case VIDEO_MIXER2_BACKGROUND_TRANSPARENT:
1057     {
1058       guint i, plane, num_planes, height;
1059
1060       num_planes = GST_VIDEO_FRAME_N_PLANES (&outframe);
1061       for (plane = 0; plane < num_planes; ++plane) {
1062         guint8 *pdata;
1063         gsize rowsize, plane_stride;
1064
1065         pdata = GST_VIDEO_FRAME_PLANE_DATA (&outframe, plane);
1066         plane_stride = GST_VIDEO_FRAME_PLANE_STRIDE (&outframe, plane);
1067         rowsize = GST_VIDEO_FRAME_COMP_WIDTH (&outframe, plane)
1068             * GST_VIDEO_FRAME_COMP_PSTRIDE (&outframe, plane);
1069         height = GST_VIDEO_FRAME_COMP_HEIGHT (&outframe, plane);
1070         for (i = 0; i < height; ++i) {
1071           memset (pdata, 0, rowsize);
1072           pdata += plane_stride;
1073         }
1074       }
1075
1076       /* use overlay to keep background transparent */
1077       composite = mix->overlay;
1078       break;
1079     }
1080   }
1081
1082   for (l = mix->sinkpads; l; l = l->next) {
1083     GstVideoMixer2Pad *pad = l->data;
1084     GstVideoMixer2Collect *mixcol = pad->mixcol;
1085
1086     if (mixcol->buffer != NULL) {
1087       GstClockTime timestamp;
1088       gint64 stream_time;
1089       GstSegment *seg;
1090       GstVideoFrame converted_frame;
1091       GstBuffer *converted_buf = NULL;
1092       GstVideoFrame frame;
1093
1094       seg = &mixcol->collect.segment;
1095
1096       timestamp = GST_BUFFER_TIMESTAMP (mixcol->buffer);
1097
1098       stream_time =
1099           gst_segment_to_stream_time (seg, GST_FORMAT_TIME, timestamp);
1100
1101       /* sync object properties on stream time */
1102       if (GST_CLOCK_TIME_IS_VALID (stream_time))
1103         gst_object_sync_values (GST_OBJECT (pad), stream_time);
1104
1105       gst_video_frame_map (&frame, &mixcol->buffer_vinfo, mixcol->buffer,
1106           GST_MAP_READ);
1107
1108       if (pad->convert) {
1109         gint converted_size;
1110
1111         /* We wait until here to set the conversion infos, in case mix->info changed */
1112         if (pad->need_conversion_update) {
1113           pad->conversion_info = mix->info;
1114           gst_video_info_set_format (&(pad->conversion_info),
1115               GST_VIDEO_INFO_FORMAT (&mix->info), pad->info.width,
1116               pad->info.height);
1117           pad->need_conversion_update = FALSE;
1118         }
1119
1120         converted_size = pad->conversion_info.size;
1121         converted_size = converted_size > outsize ? converted_size : outsize;
1122         converted_buf = gst_buffer_new_allocate (NULL, converted_size, &params);
1123
1124         gst_video_frame_map (&converted_frame, &(pad->conversion_info),
1125             converted_buf, GST_MAP_READWRITE);
1126         videomixer_videoconvert_convert_convert (pad->convert, &converted_frame,
1127             &frame);
1128         gst_video_frame_unmap (&frame);
1129       } else {
1130         converted_frame = frame;
1131       }
1132
1133       composite (&converted_frame, pad->xpos, pad->ypos, pad->alpha, &outframe);
1134
1135       if (pad->convert)
1136         gst_buffer_unref (converted_buf);
1137
1138       gst_video_frame_unmap (&converted_frame);
1139     }
1140   }
1141   gst_video_frame_unmap (&outframe);
1142
1143   return GST_FLOW_OK;
1144 }
1145
1146 /* Perform qos calculations before processing the next frame. Returns TRUE if
1147  * the frame should be processed, FALSE if the frame can be dropped entirely */
1148 static gint64
1149 gst_videomixer2_do_qos (GstVideoMixer2 * mix, GstClockTime timestamp)
1150 {
1151   GstClockTime qostime, earliest_time;
1152   gdouble proportion;
1153   gint64 jitter;
1154
1155   /* no timestamp, can't do QoS => process frame */
1156   if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (timestamp))) {
1157     GST_LOG_OBJECT (mix, "invalid timestamp, can't do QoS, process frame");
1158     return -1;
1159   }
1160
1161   /* get latest QoS observation values */
1162   gst_videomixer2_read_qos (mix, &proportion, &earliest_time);
1163
1164   /* skip qos if we have no observation (yet) => process frame */
1165   if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (earliest_time))) {
1166     GST_LOG_OBJECT (mix, "no observation yet, process frame");
1167     return -1;
1168   }
1169
1170   /* qos is done on running time */
1171   qostime =
1172       gst_segment_to_running_time (&mix->segment, GST_FORMAT_TIME, timestamp);
1173
1174   /* see how our next timestamp relates to the latest qos timestamp */
1175   GST_LOG_OBJECT (mix, "qostime %" GST_TIME_FORMAT ", earliest %"
1176       GST_TIME_FORMAT, GST_TIME_ARGS (qostime), GST_TIME_ARGS (earliest_time));
1177
1178   jitter = GST_CLOCK_DIFF (qostime, earliest_time);
1179   if (qostime != GST_CLOCK_TIME_NONE && jitter > 0) {
1180     GST_DEBUG_OBJECT (mix, "we are late, drop frame");
1181     return jitter;
1182   }
1183
1184   GST_LOG_OBJECT (mix, "process frame");
1185   return jitter;
1186 }
1187
1188 static GstFlowReturn
1189 gst_videomixer2_collected (GstCollectPads * pads, GstVideoMixer2 * mix)
1190 {
1191   GstFlowReturn ret;
1192   GstClockTime output_start_time, output_end_time;
1193   GstBuffer *outbuf = NULL;
1194   gint res;
1195   gint64 jitter;
1196
1197   /* If we're not negotiated yet... */
1198   if (GST_VIDEO_INFO_FORMAT (&mix->info) == GST_VIDEO_FORMAT_UNKNOWN)
1199     return GST_FLOW_NOT_NEGOTIATED;
1200
1201   if (mix->send_stream_start) {
1202     gchar s_id[32];
1203
1204     /* stream-start (FIXME: create id based on input ids) */
1205     g_snprintf (s_id, sizeof (s_id), "mix-%08x", g_random_int ());
1206     if (!gst_pad_push_event (mix->srcpad, gst_event_new_stream_start (s_id))) {
1207       GST_WARNING_OBJECT (mix->srcpad, "Sending stream start event failed");
1208     }
1209     mix->send_stream_start = FALSE;
1210   }
1211
1212   if (gst_pad_check_reconfigure (mix->srcpad))
1213     gst_videomixer2_update_src_caps (mix);
1214
1215   if (mix->send_caps) {
1216     if (!gst_pad_push_event (mix->srcpad,
1217             gst_event_new_caps (mix->current_caps))) {
1218       GST_WARNING_OBJECT (mix->srcpad, "Sending caps event failed");
1219     }
1220     mix->send_caps = FALSE;
1221   }
1222
1223   GST_VIDEO_MIXER2_LOCK (mix);
1224
1225   if (mix->newseg_pending) {
1226     GST_DEBUG_OBJECT (mix, "Sending NEWSEGMENT event");
1227     GST_VIDEO_MIXER2_UNLOCK (mix);
1228     if (!gst_pad_push_event (mix->srcpad,
1229             gst_event_new_segment (&mix->segment))) {
1230       ret = GST_FLOW_ERROR;
1231       goto done_unlocked;
1232     }
1233     GST_VIDEO_MIXER2_LOCK (mix);
1234     mix->newseg_pending = FALSE;
1235   }
1236
1237   if (mix->segment.position == -1)
1238     output_start_time = mix->segment.start;
1239   else
1240     output_start_time = mix->segment.position;
1241
1242   output_end_time =
1243       mix->ts_offset + gst_util_uint64_scale_round (mix->nframes + 1,
1244       GST_SECOND * GST_VIDEO_INFO_FPS_D (&mix->info),
1245       GST_VIDEO_INFO_FPS_N (&mix->info)) + mix->segment.start;
1246
1247   if (output_end_time >= mix->segment.stop) {
1248     GST_DEBUG_OBJECT (mix, "Segment done");
1249     GST_VIDEO_MIXER2_UNLOCK (mix);
1250     if (!(mix->segment.flags & GST_SEGMENT_FLAG_SEGMENT)) {
1251       gst_pad_push_event (mix->srcpad, gst_event_new_eos ());
1252
1253       ret = GST_FLOW_EOS;
1254       goto done_unlocked;
1255     }
1256   }
1257
1258   if (G_UNLIKELY (mix->pending_tags)) {
1259     gst_pad_push_event (mix->srcpad, gst_event_new_tag (mix->pending_tags));
1260     mix->pending_tags = NULL;
1261   }
1262
1263   if (mix->segment.stop != -1)
1264     output_end_time = MIN (output_end_time, mix->segment.stop);
1265
1266   res = gst_videomixer2_fill_queues (mix, output_start_time, output_end_time);
1267
1268   if (res == 0) {
1269     GST_DEBUG_OBJECT (mix, "Need more data for decisions");
1270     ret = GST_FLOW_OK;
1271     goto done;
1272   } else if (res == -1) {
1273     GST_VIDEO_MIXER2_UNLOCK (mix);
1274     GST_DEBUG_OBJECT (mix, "All sinkpads are EOS -- forwarding");
1275     gst_pad_push_event (mix->srcpad, gst_event_new_eos ());
1276     ret = GST_FLOW_EOS;
1277     goto done_unlocked;
1278   } else if (res == -2) {
1279     GST_ERROR_OBJECT (mix, "Error collecting buffers");
1280     ret = GST_FLOW_ERROR;
1281     goto done;
1282   }
1283
1284   jitter = gst_videomixer2_do_qos (mix, output_start_time);
1285   if (jitter <= 0) {
1286     ret =
1287         gst_videomixer2_blend_buffers (mix, output_start_time,
1288         output_end_time, &outbuf);
1289     mix->qos_processed++;
1290   } else {
1291     GstMessage *msg;
1292
1293     mix->qos_dropped++;
1294
1295     /* TODO: live */
1296     msg =
1297         gst_message_new_qos (GST_OBJECT_CAST (mix), FALSE,
1298         gst_segment_to_running_time (&mix->segment, GST_FORMAT_TIME,
1299             output_start_time), gst_segment_to_stream_time (&mix->segment,
1300             GST_FORMAT_TIME, output_start_time), output_start_time,
1301         output_end_time - output_start_time);
1302     gst_message_set_qos_values (msg, jitter, mix->proportion, 1000000);
1303     gst_message_set_qos_stats (msg, GST_FORMAT_BUFFERS, mix->qos_processed,
1304         mix->qos_dropped);
1305     gst_element_post_message (GST_ELEMENT_CAST (mix), msg);
1306
1307     ret = GST_FLOW_OK;
1308   }
1309
1310   mix->segment.position = output_end_time;
1311   mix->nframes++;
1312
1313   GST_VIDEO_MIXER2_UNLOCK (mix);
1314   if (outbuf) {
1315     GST_LOG_OBJECT (mix,
1316         "Pushing buffer with ts %" GST_TIME_FORMAT " and duration %"
1317         GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
1318         GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)));
1319     ret = gst_pad_push (mix->srcpad, outbuf);
1320   }
1321   goto done_unlocked;
1322
1323 done:
1324   GST_VIDEO_MIXER2_UNLOCK (mix);
1325
1326 done_unlocked:
1327   return ret;
1328 }
1329
1330 /* FIXME, the duration query should reflect how long you will produce
1331  * data, that is the amount of stream time until you will emit EOS.
1332  *
1333  * For synchronized mixing this is always the max of all the durations
1334  * of upstream since we emit EOS when all of them finished.
1335  *
1336  * We don't do synchronized mixing so this really depends on where the
1337  * streams where punched in and what their relative offsets are against
1338  * eachother which we can get from the first timestamps we see.
1339  *
1340  * When we add a new stream (or remove a stream) the duration might
1341  * also become invalid again and we need to post a new DURATION
1342  * message to notify this fact to the parent.
1343  * For now we take the max of all the upstream elements so the simple
1344  * cases work at least somewhat.
1345  */
1346 static gboolean
1347 gst_videomixer2_query_duration (GstVideoMixer2 * mix, GstQuery * query)
1348 {
1349   GValue item = { 0 };
1350   gint64 max;
1351   gboolean res;
1352   GstFormat format;
1353   GstIterator *it;
1354   gboolean done;
1355
1356   /* parse format */
1357   gst_query_parse_duration (query, &format, NULL);
1358
1359   max = -1;
1360   res = TRUE;
1361   done = FALSE;
1362
1363   /* Take maximum of all durations */
1364   it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (mix));
1365   while (!done) {
1366     switch (gst_iterator_next (it, &item)) {
1367       case GST_ITERATOR_DONE:
1368         done = TRUE;
1369         break;
1370       case GST_ITERATOR_OK:
1371       {
1372         GstPad *pad;
1373         gint64 duration;
1374
1375         pad = g_value_get_object (&item);
1376
1377         /* ask sink peer for duration */
1378         res &= gst_pad_peer_query_duration (pad, format, &duration);
1379         /* take max from all valid return values */
1380         if (res) {
1381           /* valid unknown length, stop searching */
1382           if (duration == -1) {
1383             max = duration;
1384             done = TRUE;
1385           }
1386           /* else see if bigger than current max */
1387           else if (duration > max)
1388             max = duration;
1389         }
1390         g_value_reset (&item);
1391         break;
1392       }
1393       case GST_ITERATOR_RESYNC:
1394         max = -1;
1395         res = TRUE;
1396         gst_iterator_resync (it);
1397         break;
1398       default:
1399         res = FALSE;
1400         done = TRUE;
1401         break;
1402     }
1403   }
1404   g_value_unset (&item);
1405   gst_iterator_free (it);
1406
1407   if (res) {
1408     /* and store the max */
1409     GST_DEBUG_OBJECT (mix, "Total duration in format %s: %"
1410         GST_TIME_FORMAT, gst_format_get_name (format), GST_TIME_ARGS (max));
1411     gst_query_set_duration (query, format, max);
1412   }
1413
1414   return res;
1415 }
1416
1417 static gboolean
1418 gst_videomixer2_query_latency (GstVideoMixer2 * mix, GstQuery * query)
1419 {
1420   GstClockTime min, max;
1421   gboolean live;
1422   gboolean res;
1423   GstIterator *it;
1424   gboolean done;
1425   GValue item = { 0 };
1426
1427   res = TRUE;
1428   done = FALSE;
1429   live = FALSE;
1430   min = 0;
1431   max = GST_CLOCK_TIME_NONE;
1432
1433   /* Take maximum of all latency values */
1434   it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (mix));
1435   while (!done) {
1436     switch (gst_iterator_next (it, &item)) {
1437       case GST_ITERATOR_DONE:
1438         done = TRUE;
1439         break;
1440       case GST_ITERATOR_OK:
1441       {
1442         GstPad *pad = g_value_get_object (&item);
1443         GstQuery *peerquery;
1444         GstClockTime min_cur, max_cur;
1445         gboolean live_cur;
1446
1447         peerquery = gst_query_new_latency ();
1448
1449         /* Ask peer for latency */
1450         res &= gst_pad_peer_query (pad, peerquery);
1451
1452         /* take max from all valid return values */
1453         if (res) {
1454           gst_query_parse_latency (peerquery, &live_cur, &min_cur, &max_cur);
1455
1456           if (min_cur > min)
1457             min = min_cur;
1458
1459           if (max_cur != GST_CLOCK_TIME_NONE &&
1460               ((max != GST_CLOCK_TIME_NONE && max_cur > max) ||
1461                   (max == GST_CLOCK_TIME_NONE)))
1462             max = max_cur;
1463
1464           live = live || live_cur;
1465         }
1466
1467         gst_query_unref (peerquery);
1468         g_value_reset (&item);
1469         break;
1470       }
1471       case GST_ITERATOR_RESYNC:
1472         live = FALSE;
1473         min = 0;
1474         max = GST_CLOCK_TIME_NONE;
1475         res = TRUE;
1476         gst_iterator_resync (it);
1477         break;
1478       default:
1479         res = FALSE;
1480         done = TRUE;
1481         break;
1482     }
1483   }
1484   g_value_unset (&item);
1485   gst_iterator_free (it);
1486
1487   if (res) {
1488     /* store the results */
1489     GST_DEBUG_OBJECT (mix, "Calculated total latency: live %s, min %"
1490         GST_TIME_FORMAT ", max %" GST_TIME_FORMAT,
1491         (live ? "yes" : "no"), GST_TIME_ARGS (min), GST_TIME_ARGS (max));
1492     gst_query_set_latency (query, live, min, max);
1493   }
1494
1495   return res;
1496 }
1497
1498 static gboolean
1499 gst_videomixer2_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
1500 {
1501   GstVideoMixer2 *mix = GST_VIDEO_MIXER2 (parent);
1502   gboolean res = FALSE;
1503
1504   switch (GST_QUERY_TYPE (query)) {
1505     case GST_QUERY_POSITION:
1506     {
1507       GstFormat format;
1508
1509       gst_query_parse_position (query, &format, NULL);
1510
1511       switch (format) {
1512         case GST_FORMAT_TIME:
1513           gst_query_set_position (query, format,
1514               gst_segment_to_stream_time (&mix->segment, GST_FORMAT_TIME,
1515                   mix->segment.position));
1516           res = TRUE;
1517           break;
1518         default:
1519           break;
1520       }
1521       break;
1522     }
1523     case GST_QUERY_DURATION:
1524       res = gst_videomixer2_query_duration (mix, query);
1525       break;
1526     case GST_QUERY_LATENCY:
1527       res = gst_videomixer2_query_latency (mix, query);
1528       break;
1529     case GST_QUERY_CAPS:
1530       res = gst_pad_query_default (pad, parent, query);
1531       break;
1532     default:
1533       /* FIXME, needs a custom query handler because we have multiple
1534        * sinkpads */
1535       res = FALSE;
1536       break;
1537   }
1538   return res;
1539 }
1540
1541 static gboolean
1542 gst_videomixer2_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
1543 {
1544   GstVideoMixer2 *mix = GST_VIDEO_MIXER2 (parent);
1545   gboolean result;
1546
1547   switch (GST_EVENT_TYPE (event)) {
1548     case GST_EVENT_QOS:
1549     {
1550       GstQOSType type;
1551       GstClockTimeDiff diff;
1552       GstClockTime timestamp;
1553       gdouble proportion;
1554
1555       gst_event_parse_qos (event, &type, &proportion, &diff, &timestamp);
1556
1557       gst_videomixer2_update_qos (mix, proportion, diff, timestamp);
1558
1559       result = gst_videomixer2_push_sink_event (mix, event);
1560       break;
1561     }
1562     case GST_EVENT_SEEK:
1563     {
1564       gdouble rate;
1565       GstFormat fmt;
1566       GstSeekFlags flags;
1567       GstSeekType start_type, stop_type;
1568       gint64 start, stop;
1569       GSList *l;
1570       gdouble abs_rate;
1571
1572       /* parse the seek parameters */
1573       gst_event_parse_seek (event, &rate, &fmt, &flags, &start_type,
1574           &start, &stop_type, &stop);
1575
1576       if (rate <= 0.0) {
1577         GST_ERROR_OBJECT (mix, "Negative rates not supported yet");
1578         result = FALSE;
1579         gst_event_unref (event);
1580         break;
1581       }
1582
1583       GST_DEBUG_OBJECT (mix, "Handling SEEK event");
1584
1585       abs_rate = ABS (rate);
1586
1587       GST_VIDEO_MIXER2_LOCK (mix);
1588       for (l = mix->sinkpads; l; l = l->next) {
1589         GstVideoMixer2Pad *p = l->data;
1590
1591         if (flags & GST_SEEK_FLAG_FLUSH) {
1592           gst_buffer_replace (&p->mixcol->buffer, NULL);
1593           p->mixcol->start_time = p->mixcol->end_time = -1;
1594           continue;
1595         }
1596
1597         /* Convert to the output segment rate */
1598         if (ABS (mix->segment.rate) != abs_rate) {
1599           if (ABS (mix->segment.rate) != 1.0 && p->mixcol->buffer) {
1600             p->mixcol->start_time /= ABS (mix->segment.rate);
1601             p->mixcol->end_time /= ABS (mix->segment.rate);
1602           }
1603           if (abs_rate != 1.0 && p->mixcol->buffer) {
1604             p->mixcol->start_time *= abs_rate;
1605             p->mixcol->end_time *= abs_rate;
1606           }
1607         }
1608       }
1609       GST_VIDEO_MIXER2_UNLOCK (mix);
1610
1611       gst_segment_do_seek (&mix->segment, rate, fmt, flags, start_type, start,
1612           stop_type, stop, NULL);
1613       mix->segment.position = -1;
1614       mix->ts_offset = 0;
1615       mix->nframes = 0;
1616       mix->newseg_pending = TRUE;
1617
1618       gst_videomixer2_reset_qos (mix);
1619
1620       result = gst_collect_pads_src_event_default (mix->collect, pad, event);
1621       break;
1622     }
1623     case GST_EVENT_NAVIGATION:
1624       /* navigation is rather pointless. */
1625       result = FALSE;
1626       gst_event_unref (event);
1627       break;
1628     default:
1629       /* just forward the rest for now */
1630       result = gst_videomixer2_push_sink_event (mix, event);
1631       break;
1632   }
1633
1634   return result;
1635 }
1636
1637 static gboolean
1638 gst_videomixer2_src_setcaps (GstPad * pad, GstVideoMixer2 * mix, GstCaps * caps)
1639 {
1640   gboolean ret = FALSE;
1641   GstVideoInfo info;
1642
1643   GST_INFO_OBJECT (pad, "set src caps: %" GST_PTR_FORMAT, caps);
1644
1645   if (!gst_video_info_from_caps (&info, caps))
1646     goto done;
1647
1648   GST_VIDEO_MIXER2_LOCK (mix);
1649
1650   mix->blend = NULL;
1651   mix->overlay = NULL;
1652   mix->fill_checker = NULL;
1653   mix->fill_color = NULL;
1654
1655   if (GST_VIDEO_INFO_FPS_N (&mix->info) != GST_VIDEO_INFO_FPS_N (&info) ||
1656       GST_VIDEO_INFO_FPS_D (&mix->info) != GST_VIDEO_INFO_FPS_D (&info)) {
1657     if (mix->segment.position != -1) {
1658       mix->ts_offset = mix->segment.position - mix->segment.start;
1659       mix->nframes = 0;
1660     }
1661     gst_videomixer2_reset_qos (mix);
1662   }
1663
1664   mix->info = info;
1665
1666   switch (GST_VIDEO_INFO_FORMAT (&mix->info)) {
1667     case GST_VIDEO_FORMAT_AYUV:
1668       mix->blend = gst_video_mixer_blend_ayuv;
1669       mix->overlay = gst_video_mixer_overlay_ayuv;
1670       mix->fill_checker = gst_video_mixer_fill_checker_ayuv;
1671       mix->fill_color = gst_video_mixer_fill_color_ayuv;
1672       ret = TRUE;
1673       break;
1674     case GST_VIDEO_FORMAT_ARGB:
1675       mix->blend = gst_video_mixer_blend_argb;
1676       mix->overlay = gst_video_mixer_overlay_argb;
1677       mix->fill_checker = gst_video_mixer_fill_checker_argb;
1678       mix->fill_color = gst_video_mixer_fill_color_argb;
1679       ret = TRUE;
1680       break;
1681     case GST_VIDEO_FORMAT_BGRA:
1682       mix->blend = gst_video_mixer_blend_bgra;
1683       mix->overlay = gst_video_mixer_overlay_bgra;
1684       mix->fill_checker = gst_video_mixer_fill_checker_bgra;
1685       mix->fill_color = gst_video_mixer_fill_color_bgra;
1686       ret = TRUE;
1687       break;
1688     case GST_VIDEO_FORMAT_ABGR:
1689       mix->blend = gst_video_mixer_blend_abgr;
1690       mix->overlay = gst_video_mixer_overlay_abgr;
1691       mix->fill_checker = gst_video_mixer_fill_checker_abgr;
1692       mix->fill_color = gst_video_mixer_fill_color_abgr;
1693       ret = TRUE;
1694       break;
1695     case GST_VIDEO_FORMAT_RGBA:
1696       mix->blend = gst_video_mixer_blend_rgba;
1697       mix->overlay = gst_video_mixer_overlay_rgba;
1698       mix->fill_checker = gst_video_mixer_fill_checker_rgba;
1699       mix->fill_color = gst_video_mixer_fill_color_rgba;
1700       ret = TRUE;
1701       break;
1702     case GST_VIDEO_FORMAT_Y444:
1703       mix->blend = gst_video_mixer_blend_y444;
1704       mix->overlay = mix->blend;
1705       mix->fill_checker = gst_video_mixer_fill_checker_y444;
1706       mix->fill_color = gst_video_mixer_fill_color_y444;
1707       ret = TRUE;
1708       break;
1709     case GST_VIDEO_FORMAT_Y42B:
1710       mix->blend = gst_video_mixer_blend_y42b;
1711       mix->overlay = mix->blend;
1712       mix->fill_checker = gst_video_mixer_fill_checker_y42b;
1713       mix->fill_color = gst_video_mixer_fill_color_y42b;
1714       ret = TRUE;
1715       break;
1716     case GST_VIDEO_FORMAT_YUY2:
1717       mix->blend = gst_video_mixer_blend_yuy2;
1718       mix->overlay = mix->blend;
1719       mix->fill_checker = gst_video_mixer_fill_checker_yuy2;
1720       mix->fill_color = gst_video_mixer_fill_color_yuy2;
1721       ret = TRUE;
1722       break;
1723     case GST_VIDEO_FORMAT_UYVY:
1724       mix->blend = gst_video_mixer_blend_uyvy;
1725       mix->overlay = mix->blend;
1726       mix->fill_checker = gst_video_mixer_fill_checker_uyvy;
1727       mix->fill_color = gst_video_mixer_fill_color_uyvy;
1728       ret = TRUE;
1729       break;
1730     case GST_VIDEO_FORMAT_YVYU:
1731       mix->blend = gst_video_mixer_blend_yvyu;
1732       mix->overlay = mix->blend;
1733       mix->fill_checker = gst_video_mixer_fill_checker_yvyu;
1734       mix->fill_color = gst_video_mixer_fill_color_yvyu;
1735       ret = TRUE;
1736       break;
1737     case GST_VIDEO_FORMAT_I420:
1738       mix->blend = gst_video_mixer_blend_i420;
1739       mix->overlay = mix->blend;
1740       mix->fill_checker = gst_video_mixer_fill_checker_i420;
1741       mix->fill_color = gst_video_mixer_fill_color_i420;
1742       ret = TRUE;
1743       break;
1744     case GST_VIDEO_FORMAT_YV12:
1745       mix->blend = gst_video_mixer_blend_yv12;
1746       mix->overlay = mix->blend;
1747       mix->fill_checker = gst_video_mixer_fill_checker_yv12;
1748       mix->fill_color = gst_video_mixer_fill_color_yv12;
1749       ret = TRUE;
1750       break;
1751     case GST_VIDEO_FORMAT_NV12:
1752       mix->blend = gst_video_mixer_blend_nv12;
1753       mix->overlay = mix->blend;
1754       mix->fill_checker = gst_video_mixer_fill_checker_nv12;
1755       mix->fill_color = gst_video_mixer_fill_color_nv12;
1756       ret = TRUE;
1757       break;
1758     case GST_VIDEO_FORMAT_NV21:
1759       mix->blend = gst_video_mixer_blend_nv21;
1760       mix->overlay = mix->blend;
1761       mix->fill_checker = gst_video_mixer_fill_checker_nv21;
1762       mix->fill_color = gst_video_mixer_fill_color_nv21;
1763       ret = TRUE;
1764       break;
1765     case GST_VIDEO_FORMAT_Y41B:
1766       mix->blend = gst_video_mixer_blend_y41b;
1767       mix->overlay = mix->blend;
1768       mix->fill_checker = gst_video_mixer_fill_checker_y41b;
1769       mix->fill_color = gst_video_mixer_fill_color_y41b;
1770       ret = TRUE;
1771       break;
1772     case GST_VIDEO_FORMAT_RGB:
1773       mix->blend = gst_video_mixer_blend_rgb;
1774       mix->overlay = mix->blend;
1775       mix->fill_checker = gst_video_mixer_fill_checker_rgb;
1776       mix->fill_color = gst_video_mixer_fill_color_rgb;
1777       ret = TRUE;
1778       break;
1779     case GST_VIDEO_FORMAT_BGR:
1780       mix->blend = gst_video_mixer_blend_bgr;
1781       mix->overlay = mix->blend;
1782       mix->fill_checker = gst_video_mixer_fill_checker_bgr;
1783       mix->fill_color = gst_video_mixer_fill_color_bgr;
1784       ret = TRUE;
1785       break;
1786     case GST_VIDEO_FORMAT_xRGB:
1787       mix->blend = gst_video_mixer_blend_xrgb;
1788       mix->overlay = mix->blend;
1789       mix->fill_checker = gst_video_mixer_fill_checker_xrgb;
1790       mix->fill_color = gst_video_mixer_fill_color_xrgb;
1791       ret = TRUE;
1792       break;
1793     case GST_VIDEO_FORMAT_xBGR:
1794       mix->blend = gst_video_mixer_blend_xbgr;
1795       mix->overlay = mix->blend;
1796       mix->fill_checker = gst_video_mixer_fill_checker_xbgr;
1797       mix->fill_color = gst_video_mixer_fill_color_xbgr;
1798       ret = TRUE;
1799       break;
1800     case GST_VIDEO_FORMAT_RGBx:
1801       mix->blend = gst_video_mixer_blend_rgbx;
1802       mix->overlay = mix->blend;
1803       mix->fill_checker = gst_video_mixer_fill_checker_rgbx;
1804       mix->fill_color = gst_video_mixer_fill_color_rgbx;
1805       ret = TRUE;
1806       break;
1807     case GST_VIDEO_FORMAT_BGRx:
1808       mix->blend = gst_video_mixer_blend_bgrx;
1809       mix->overlay = mix->blend;
1810       mix->fill_checker = gst_video_mixer_fill_checker_bgrx;
1811       mix->fill_color = gst_video_mixer_fill_color_bgrx;
1812       ret = TRUE;
1813       break;
1814     default:
1815       break;
1816   }
1817   GST_VIDEO_MIXER2_UNLOCK (mix);
1818
1819   if (mix->current_caps == NULL ||
1820       gst_caps_is_equal (caps, mix->current_caps) == FALSE) {
1821     gst_caps_replace (&mix->current_caps, caps);
1822     mix->send_caps = TRUE;
1823   }
1824
1825 done:
1826   return ret;
1827 }
1828
1829 static GstFlowReturn
1830 gst_videomixer2_sink_clip (GstCollectPads * pads,
1831     GstCollectData * data, GstBuffer * buf, GstBuffer ** outbuf,
1832     GstVideoMixer2 * mix)
1833 {
1834   GstVideoMixer2Pad *pad = GST_VIDEO_MIXER2_PAD (data->pad);
1835   GstVideoMixer2Collect *mixcol = pad->mixcol;
1836   GstClockTime start_time, end_time;
1837
1838   start_time = GST_BUFFER_TIMESTAMP (buf);
1839   if (start_time == -1) {
1840     GST_ERROR_OBJECT (pad, "Timestamped buffers required!");
1841     gst_buffer_unref (buf);
1842     return GST_FLOW_ERROR;
1843   }
1844
1845   end_time = GST_BUFFER_DURATION (buf);
1846   if (end_time == -1 && GST_VIDEO_INFO_FPS_N (&pad->info) != 0)
1847     end_time =
1848         gst_util_uint64_scale_int_round (GST_SECOND,
1849         GST_VIDEO_INFO_FPS_D (&pad->info), GST_VIDEO_INFO_FPS_N (&pad->info));
1850   if (end_time == -1) {
1851     *outbuf = buf;
1852     return GST_FLOW_OK;
1853   }
1854
1855   start_time = MAX (start_time, mixcol->collect.segment.start);
1856   start_time =
1857       gst_segment_to_running_time (&mixcol->collect.segment,
1858       GST_FORMAT_TIME, start_time);
1859
1860   end_time += GST_BUFFER_TIMESTAMP (buf);
1861   if (mixcol->collect.segment.stop != -1)
1862     end_time = MIN (end_time, mixcol->collect.segment.stop);
1863   end_time =
1864       gst_segment_to_running_time (&mixcol->collect.segment,
1865       GST_FORMAT_TIME, end_time);
1866
1867   /* Convert to the output segment rate */
1868   if (ABS (mix->segment.rate) != 1.0) {
1869     start_time *= ABS (mix->segment.rate);
1870     end_time *= ABS (mix->segment.rate);
1871   }
1872
1873   if (mixcol->buffer != NULL && end_time < mixcol->end_time) {
1874     gst_buffer_unref (buf);
1875     *outbuf = NULL;
1876     return GST_FLOW_OK;
1877   }
1878
1879   *outbuf = buf;
1880   return GST_FLOW_OK;
1881 }
1882
1883 static void
1884 gst_videomixer2_flush (GstCollectPads * pads, GstVideoMixer2 * mix)
1885 {
1886   if (mix->pending_tags) {
1887     gst_tag_list_unref (mix->pending_tags);
1888     mix->pending_tags = NULL;
1889   }
1890 }
1891
1892 static gboolean
1893 gst_videomixer2_sink_event (GstCollectPads * pads, GstCollectData * cdata,
1894     GstEvent * event, GstVideoMixer2 * mix)
1895 {
1896   GstVideoMixer2Pad *pad = GST_VIDEO_MIXER2_PAD (cdata->pad);
1897   gboolean ret = TRUE, discard = FALSE;
1898
1899   GST_DEBUG_OBJECT (pad, "Got %s event: %" GST_PTR_FORMAT,
1900       GST_EVENT_TYPE_NAME (event), event);
1901
1902   switch (GST_EVENT_TYPE (event)) {
1903     case GST_EVENT_CAPS:
1904     {
1905       GstCaps *caps;
1906
1907       gst_event_parse_caps (event, &caps);
1908       ret =
1909           gst_videomixer2_pad_sink_setcaps (GST_PAD (pad), GST_OBJECT (mix),
1910           caps);
1911       gst_event_unref (event);
1912       event = NULL;
1913       break;
1914     }
1915     case GST_EVENT_SEGMENT:{
1916       GstSegment seg;
1917       gst_event_copy_segment (event, &seg);
1918
1919       g_assert (seg.format == GST_FORMAT_TIME);
1920       break;
1921     }
1922     case GST_EVENT_FLUSH_STOP:
1923       mix->newseg_pending = TRUE;
1924
1925       gst_videomixer2_reset_qos (mix);
1926       gst_buffer_replace (&pad->mixcol->buffer, NULL);
1927       pad->mixcol->start_time = -1;
1928       pad->mixcol->end_time = -1;
1929
1930       mix->segment.position = -1;
1931       mix->ts_offset = 0;
1932       mix->nframes = 0;
1933       break;
1934     case GST_EVENT_TAG:
1935     {
1936       /* collect tags here so we can push them out when we collect data */
1937       GstTagList *tags;
1938
1939       gst_event_parse_tag (event, &tags);
1940       tags = gst_tag_list_merge (mix->pending_tags, tags, GST_TAG_MERGE_APPEND);
1941       if (mix->pending_tags)
1942         gst_tag_list_unref (mix->pending_tags);
1943       mix->pending_tags = tags;
1944       event = NULL;
1945       break;
1946     }
1947     default:
1948       break;
1949   }
1950
1951   if (event != NULL)
1952     return gst_collect_pads_event_default (pads, cdata, event, discard);
1953
1954   return ret;
1955 }
1956
1957 static gboolean
1958 forward_event_func (GValue * item, GValue * ret, GstEvent * event)
1959 {
1960   GstPad *pad = g_value_get_object (item);
1961   gst_event_ref (event);
1962   GST_LOG_OBJECT (pad, "About to send event %s", GST_EVENT_TYPE_NAME (event));
1963   if (!gst_pad_push_event (pad, event)) {
1964     g_value_set_boolean (ret, FALSE);
1965     GST_WARNING_OBJECT (pad, "Sending event  %p (%s) failed.",
1966         event, GST_EVENT_TYPE_NAME (event));
1967   } else {
1968     GST_LOG_OBJECT (pad, "Sent event  %p (%s).",
1969         event, GST_EVENT_TYPE_NAME (event));
1970   }
1971   return TRUE;
1972 }
1973
1974 static gboolean
1975 gst_videomixer2_push_sink_event (GstVideoMixer2 * mix, GstEvent * event)
1976 {
1977   GstIterator *it;
1978   GValue vret = { 0 };
1979
1980   GST_LOG_OBJECT (mix, "Forwarding event %p (%s)", event,
1981       GST_EVENT_TYPE_NAME (event));
1982
1983   g_value_init (&vret, G_TYPE_BOOLEAN);
1984   g_value_set_boolean (&vret, TRUE);
1985   it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (mix));
1986   gst_iterator_fold (it, (GstIteratorFoldFunction) forward_event_func, &vret,
1987       event);
1988   gst_iterator_free (it);
1989   gst_event_unref (event);
1990
1991   return g_value_get_boolean (&vret);
1992 }
1993
1994 /* GstElement vmethods */
1995 static GstStateChangeReturn
1996 gst_videomixer2_change_state (GstElement * element, GstStateChange transition)
1997 {
1998   GstVideoMixer2 *mix = GST_VIDEO_MIXER2 (element);
1999   GstStateChangeReturn ret;
2000
2001   switch (transition) {
2002     case GST_STATE_CHANGE_READY_TO_PAUSED:
2003       mix->send_stream_start = TRUE;
2004       mix->send_caps = TRUE;
2005       gst_segment_init (&mix->segment, GST_FORMAT_TIME);
2006       gst_caps_replace (&mix->current_caps, NULL);
2007       GST_LOG_OBJECT (mix, "starting collectpads");
2008       gst_collect_pads_start (mix->collect);
2009       break;
2010     case GST_STATE_CHANGE_PAUSED_TO_READY:
2011       GST_LOG_OBJECT (mix, "stopping collectpads");
2012       gst_collect_pads_stop (mix->collect);
2013       break;
2014     default:
2015       break;
2016   }
2017
2018   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2019
2020   switch (transition) {
2021     case GST_STATE_CHANGE_PAUSED_TO_READY:
2022       gst_videomixer2_reset (mix);
2023       break;
2024     default:
2025       break;
2026   }
2027
2028   return ret;
2029 }
2030
2031 static GstPad *
2032 gst_videomixer2_request_new_pad (GstElement * element,
2033     GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
2034 {
2035   GstVideoMixer2 *mix;
2036   GstVideoMixer2Pad *mixpad;
2037   GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
2038
2039   mix = GST_VIDEO_MIXER2 (element);
2040
2041   if (templ == gst_element_class_get_pad_template (klass, "sink_%u")) {
2042     guint serial = 0;
2043     gchar *name = NULL;
2044     GstVideoMixer2Collect *mixcol = NULL;
2045
2046     GST_VIDEO_MIXER2_LOCK (mix);
2047     if (req_name == NULL || strlen (req_name) < 6
2048         || !g_str_has_prefix (req_name, "sink_")) {
2049       /* no name given when requesting the pad, use next available int */
2050       serial = mix->next_sinkpad++;
2051     } else {
2052       /* parse serial number from requested padname */
2053       serial = g_ascii_strtoull (&req_name[5], NULL, 10);
2054       if (serial >= mix->next_sinkpad)
2055         mix->next_sinkpad = serial + 1;
2056     }
2057     /* create new pad with the name */
2058     name = g_strdup_printf ("sink_%u", serial);
2059     mixpad = g_object_new (GST_TYPE_VIDEO_MIXER2_PAD, "name", name, "direction",
2060         templ->direction, "template", templ, NULL);
2061     g_free (name);
2062
2063     mixpad->zorder = mix->numpads;
2064     mixpad->xpos = DEFAULT_PAD_XPOS;
2065     mixpad->ypos = DEFAULT_PAD_YPOS;
2066     mixpad->alpha = DEFAULT_PAD_ALPHA;
2067
2068     mixcol = (GstVideoMixer2Collect *)
2069         gst_collect_pads_add_pad (mix->collect, GST_PAD (mixpad),
2070         sizeof (GstVideoMixer2Collect),
2071         (GstCollectDataDestroyNotify) gst_videomixer2_collect_free, TRUE);
2072
2073     /* Keep track of each other */
2074     mixcol->mixpad = mixpad;
2075     mixpad->mixcol = mixcol;
2076
2077     mixcol->start_time = -1;
2078     mixcol->end_time = -1;
2079
2080     /* Keep an internal list of mixpads for zordering */
2081     mix->sinkpads = g_slist_insert_sorted (mix->sinkpads, mixpad,
2082         (GCompareFunc) pad_zorder_compare);
2083     mix->numpads++;
2084     GST_VIDEO_MIXER2_UNLOCK (mix);
2085   } else {
2086     return NULL;
2087   }
2088
2089   GST_DEBUG_OBJECT (element, "Adding pad %s", GST_PAD_NAME (mixpad));
2090
2091   /* add the pad to the element */
2092   gst_element_add_pad (element, GST_PAD (mixpad));
2093   gst_child_proxy_child_added (GST_CHILD_PROXY (mix), G_OBJECT (mixpad),
2094       GST_OBJECT_NAME (mixpad));
2095
2096   return GST_PAD (mixpad);
2097 }
2098
2099 static void
2100 gst_videomixer2_release_pad (GstElement * element, GstPad * pad)
2101 {
2102   GstVideoMixer2 *mix = NULL;
2103   GstVideoMixer2Pad *mixpad;
2104   gboolean update_caps;
2105
2106   mix = GST_VIDEO_MIXER2 (element);
2107
2108   GST_VIDEO_MIXER2_LOCK (mix);
2109   if (G_UNLIKELY (g_slist_find (mix->sinkpads, pad) == NULL)) {
2110     g_warning ("Unknown pad %s", GST_PAD_NAME (pad));
2111     goto error;
2112   }
2113
2114   mixpad = GST_VIDEO_MIXER2_PAD (pad);
2115
2116   if (mixpad->convert)
2117     videomixer_videoconvert_convert_free (mixpad->convert);
2118
2119   mix->sinkpads = g_slist_remove (mix->sinkpads, pad);
2120   gst_child_proxy_child_removed (GST_CHILD_PROXY (mix), G_OBJECT (mixpad),
2121       GST_OBJECT_NAME (mixpad));
2122   mix->numpads--;
2123
2124   GST_COLLECT_PADS_STREAM_LOCK (mix->collect);
2125   gst_videomixer2_update_converters (mix);
2126   GST_COLLECT_PADS_STREAM_UNLOCK (mix->collect);
2127
2128   update_caps = GST_VIDEO_INFO_FORMAT (&mix->info) != GST_VIDEO_FORMAT_UNKNOWN;
2129   GST_VIDEO_MIXER2_UNLOCK (mix);
2130
2131   gst_collect_pads_remove_pad (mix->collect, pad);
2132
2133   if (update_caps)
2134     gst_videomixer2_update_src_caps (mix);
2135
2136   gst_element_remove_pad (element, pad);
2137   return;
2138 error:
2139   GST_VIDEO_MIXER2_UNLOCK (mix);
2140 }
2141
2142 /* GObject vmethods */
2143 static void
2144 gst_videomixer2_finalize (GObject * o)
2145 {
2146   GstVideoMixer2 *mix = GST_VIDEO_MIXER2 (o);
2147
2148   gst_object_unref (mix->collect);
2149   g_mutex_clear (&mix->lock);
2150   g_mutex_clear (&mix->setcaps_lock);
2151
2152   G_OBJECT_CLASS (parent_class)->finalize (o);
2153 }
2154
2155 static void
2156 gst_videomixer2_dispose (GObject * o)
2157 {
2158   GstVideoMixer2 *mix = GST_VIDEO_MIXER2 (o);
2159   GSList *tmp;
2160
2161   for (tmp = mix->sinkpads; tmp; tmp = tmp->next) {
2162     GstVideoMixer2Pad *mixpad = tmp->data;
2163
2164     if (mixpad->convert)
2165       videomixer_videoconvert_convert_free (mixpad->convert);
2166   }
2167
2168   if (mix->pending_tags) {
2169     gst_tag_list_unref (mix->pending_tags);
2170     mix->pending_tags = NULL;
2171   }
2172
2173   gst_caps_replace (&mix->current_caps, NULL);
2174
2175   G_OBJECT_CLASS (parent_class)->dispose (o);
2176 }
2177
2178 static void
2179 gst_videomixer2_get_property (GObject * object,
2180     guint prop_id, GValue * value, GParamSpec * pspec)
2181 {
2182   GstVideoMixer2 *mix = GST_VIDEO_MIXER2 (object);
2183
2184   switch (prop_id) {
2185     case PROP_BACKGROUND:
2186       g_value_set_enum (value, mix->background);
2187       break;
2188     default:
2189       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2190       break;
2191   }
2192 }
2193
2194 static void
2195 gst_videomixer2_set_property (GObject * object,
2196     guint prop_id, const GValue * value, GParamSpec * pspec)
2197 {
2198   GstVideoMixer2 *mix = GST_VIDEO_MIXER2 (object);
2199
2200   switch (prop_id) {
2201     case PROP_BACKGROUND:
2202       mix->background = g_value_get_enum (value);
2203       break;
2204     default:
2205       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2206       break;
2207   }
2208 }
2209
2210 /* GstChildProxy implementation */
2211 static GObject *
2212 gst_videomixer2_child_proxy_get_child_by_index (GstChildProxy * child_proxy,
2213     guint index)
2214 {
2215   GstVideoMixer2 *mix = GST_VIDEO_MIXER2 (child_proxy);
2216   GObject *obj;
2217
2218   GST_VIDEO_MIXER2_LOCK (mix);
2219   if ((obj = g_slist_nth_data (mix->sinkpads, index)))
2220     g_object_ref (obj);
2221   GST_VIDEO_MIXER2_UNLOCK (mix);
2222   return obj;
2223 }
2224
2225 static guint
2226 gst_videomixer2_child_proxy_get_children_count (GstChildProxy * child_proxy)
2227 {
2228   guint count = 0;
2229   GstVideoMixer2 *mix = GST_VIDEO_MIXER2 (child_proxy);
2230
2231   GST_VIDEO_MIXER2_LOCK (mix);
2232   count = mix->numpads;
2233   GST_VIDEO_MIXER2_UNLOCK (mix);
2234   GST_INFO_OBJECT (mix, "Children Count: %d", count);
2235   return count;
2236 }
2237
2238 static void
2239 gst_videomixer2_child_proxy_init (gpointer g_iface, gpointer iface_data)
2240 {
2241   GstChildProxyInterface *iface = g_iface;
2242
2243   GST_INFO ("intializing child proxy interface");
2244   iface->get_child_by_index = gst_videomixer2_child_proxy_get_child_by_index;
2245   iface->get_children_count = gst_videomixer2_child_proxy_get_children_count;
2246 }
2247
2248 static void
2249 gst_videomixer2_constructed (GObject * obj)
2250 {
2251   GstVideoMixer2 *mix = GST_VIDEO_MIXER2 (obj);
2252   gchar *cp_name;
2253
2254   cp_name = g_strconcat (GST_OBJECT_NAME (obj), "-collectpads", NULL);
2255   gst_object_set_name (GST_OBJECT (mix->collect), cp_name);
2256   g_free (cp_name);
2257
2258   G_OBJECT_CLASS (gst_videomixer2_parent_class)->constructed (obj);
2259 }
2260
2261 /* GObject boilerplate */
2262 static void
2263 gst_videomixer2_class_init (GstVideoMixer2Class * klass)
2264 {
2265   GObjectClass *gobject_class = (GObjectClass *) klass;
2266   GstElementClass *gstelement_class = (GstElementClass *) klass;
2267
2268   gobject_class->constructed = gst_videomixer2_constructed;
2269   gobject_class->finalize = gst_videomixer2_finalize;
2270   gobject_class->dispose = gst_videomixer2_dispose;
2271
2272   gobject_class->get_property = gst_videomixer2_get_property;
2273   gobject_class->set_property = gst_videomixer2_set_property;
2274
2275   g_object_class_install_property (gobject_class, PROP_BACKGROUND,
2276       g_param_spec_enum ("background", "Background", "Background type",
2277           GST_TYPE_VIDEO_MIXER2_BACKGROUND,
2278           DEFAULT_BACKGROUND, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2279
2280   gstelement_class->request_new_pad =
2281       GST_DEBUG_FUNCPTR (gst_videomixer2_request_new_pad);
2282   gstelement_class->release_pad =
2283       GST_DEBUG_FUNCPTR (gst_videomixer2_release_pad);
2284   gstelement_class->change_state =
2285       GST_DEBUG_FUNCPTR (gst_videomixer2_change_state);
2286
2287   gst_element_class_add_pad_template (gstelement_class,
2288       gst_static_pad_template_get (&src_factory));
2289   gst_element_class_add_pad_template (gstelement_class,
2290       gst_static_pad_template_get (&sink_factory));
2291
2292   gst_element_class_set_static_metadata (gstelement_class, "Video mixer 2",
2293       "Filter/Editor/Video/Compositor",
2294       "Mix multiple video streams", "Wim Taymans <wim@fluendo.com>, "
2295       "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
2296
2297   /* Register the pad class */
2298   g_type_class_ref (GST_TYPE_VIDEO_MIXER2_PAD);
2299 }
2300
2301 static void
2302 gst_videomixer2_init (GstVideoMixer2 * mix)
2303 {
2304   GstElementClass *klass = GST_ELEMENT_GET_CLASS (mix);
2305
2306   mix->srcpad =
2307       gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
2308           "src"), "src");
2309   gst_pad_set_query_function (GST_PAD (mix->srcpad),
2310       GST_DEBUG_FUNCPTR (gst_videomixer2_src_query));
2311   gst_pad_set_event_function (GST_PAD (mix->srcpad),
2312       GST_DEBUG_FUNCPTR (gst_videomixer2_src_event));
2313   gst_element_add_pad (GST_ELEMENT (mix), mix->srcpad);
2314
2315   mix->collect = gst_collect_pads_new ();
2316   gst_collect_pads_set_flush_function (mix->collect,
2317       (GstCollectPadsFlushFunction) gst_videomixer2_flush, mix);
2318   mix->background = DEFAULT_BACKGROUND;
2319   mix->current_caps = NULL;
2320   mix->pending_tags = NULL;
2321
2322   gst_collect_pads_set_function (mix->collect,
2323       (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_videomixer2_collected),
2324       mix);
2325   gst_collect_pads_set_event_function (mix->collect,
2326       (GstCollectPadsEventFunction) gst_videomixer2_sink_event, mix);
2327   gst_collect_pads_set_query_function (mix->collect,
2328       (GstCollectPadsQueryFunction) gst_videomixer2_sink_query, mix);
2329   gst_collect_pads_set_clip_function (mix->collect,
2330       (GstCollectPadsClipFunction) gst_videomixer2_sink_clip, mix);
2331
2332   g_mutex_init (&mix->lock);
2333   g_mutex_init (&mix->setcaps_lock);
2334   /* initialize variables */
2335   gst_videomixer2_reset (mix);
2336 }
2337
2338 /* Element registration */
2339 static gboolean
2340 plugin_init (GstPlugin * plugin)
2341 {
2342   GST_DEBUG_CATEGORY_INIT (gst_videomixer2_debug, "videomixer", 0,
2343       "video mixer");
2344
2345   gst_video_mixer_init_blend ();
2346
2347   return gst_element_register (plugin, "videomixer", GST_RANK_PRIMARY,
2348       GST_TYPE_VIDEO_MIXER2);
2349 }
2350
2351 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
2352     GST_VERSION_MINOR,
2353     videomixer,
2354     "Video mixer", plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME,
2355     GST_PACKAGE_ORIGIN)