compositor: Add support for I420/I422 high bitdepth formats
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-base / gst / compositor / compositor.c
1 /* Video compositor plugin
2  * Copyright (C) 2004, 2008 Wim Taymans <wim@fluendo.com>
3  * Copyright (C) 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
4  * Copyright (C) 2014 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
5  * Copyright (C) 2014 Thibault Saunier <tsaunier@gnome.org>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 /**
24  * SECTION:element-compositor
25  * @title: compositor
26  *
27  * Compositor can accept AYUV, VUYA, ARGB and BGRA video streams. For each of the requested
28  * sink pads it will compare the incoming geometry and framerate to define the
29  * output parameters. Indeed output video frames will have the geometry of the
30  * biggest incoming video stream and the framerate of the fastest incoming one.
31  *
32  * Compositor will do colorspace conversion.
33  *
34  * Individual parameters for each input stream can be configured on the
35  * #GstCompositorPad:
36  *
37  * * "xpos": The x-coordinate position of the top-left corner of the picture (#gint)
38  * * "ypos": The y-coordinate position of the top-left corner of the picture (#gint)
39  * * "width": The width of the picture; the input will be scaled if necessary (#gint)
40  * * "height": The height of the picture; the input will be scaled if necessary (#gint)
41  * * "alpha": The transparency of the picture; between 0.0 and 1.0. The blending
42  *   is a simple copy when fully-transparent (0.0) and fully-opaque (1.0). (#gdouble)
43  * * "zorder": The z-order position of the picture in the composition (#guint)
44  *
45  * ## Sample pipelines
46  * |[
47  * gst-launch-1.0 \
48  *   videotestsrc pattern=1 ! \
49  *   video/x-raw,format=AYUV,framerate=\(fraction\)10/1,width=100,height=100 ! \
50  *   videobox border-alpha=0 top=-70 bottom=-70 right=-220 ! \
51  *   compositor name=comp sink_0::alpha=0.7 sink_1::alpha=0.5 ! \
52  *   videoconvert ! xvimagesink \
53  *   videotestsrc ! \
54  *   video/x-raw,format=AYUV,framerate=\(fraction\)5/1,width=320,height=240 ! comp.
55  * ]| A pipeline to demonstrate compositor used together with videobox.
56  * This should show a 320x240 pixels video test source with some transparency
57  * showing the background checker pattern. Another video test source with just
58  * the snow pattern of 100x100 pixels is overlaid on top of the first one on
59  * the left vertically centered with a small transparency showing the first
60  * video test source behind and the checker pattern under it. Note that the
61  * framerate of the output video is 10 frames per second.
62  * |[
63  * gst-launch-1.0 videotestsrc pattern=1 ! \
64  *   video/x-raw, framerate=\(fraction\)10/1, width=100, height=100 ! \
65  *   compositor name=comp ! videoconvert ! ximagesink \
66  *   videotestsrc !  \
67  *   video/x-raw, framerate=\(fraction\)5/1, width=320, height=240 ! comp.
68  * ]| A pipeline to demonstrate bgra comping. (This does not demonstrate alpha blending).
69  * |[
70  * gst-launch-1.0 videotestsrc pattern=1 ! \
71  *   video/x-raw,format =I420, framerate=\(fraction\)10/1, width=100, height=100 ! \
72  *   compositor name=comp ! videoconvert ! ximagesink \
73  *   videotestsrc ! \
74  *   video/x-raw,format=I420, framerate=\(fraction\)5/1, width=320, height=240 ! comp.
75  * ]| A pipeline to test I420
76  * |[
77  * gst-launch-1.0 compositor name=comp sink_1::alpha=0.5 sink_1::xpos=50 sink_1::ypos=50 ! \
78  *   videoconvert ! ximagesink \
79  *   videotestsrc pattern=snow timestamp-offset=3000000000 ! \
80  *   "video/x-raw,format=AYUV,width=640,height=480,framerate=(fraction)30/1" ! \
81  *   timeoverlay ! queue2 ! comp. \
82  *   videotestsrc pattern=smpte ! \
83  *   "video/x-raw,format=AYUV,width=800,height=600,framerate=(fraction)10/1" ! \
84  *   timeoverlay ! queue2 ! comp.
85  * ]| A pipeline to demonstrate synchronized compositing (the second stream starts after 3 seconds)
86  *
87  */
88
89 #ifdef HAVE_CONFIG_H
90 #include "config.h"
91 #endif
92
93 #include <string.h>
94
95 #include "compositor.h"
96
97 #ifdef DISABLE_ORC
98 #define orc_memset memset
99 #else
100 #include <orc/orcfunctions.h>
101 #endif
102
103 GST_DEBUG_CATEGORY_STATIC (gst_compositor_debug);
104 #define GST_CAT_DEFAULT gst_compositor_debug
105
106 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
107 #define FORMATS " { AYUV, VUYA, BGRA, ARGB, RGBA, ABGR, Y444, Y42B, YUY2, UYVY, "\
108                 "   YVYU, I422_12LE, I422_12BE, I422_10LE, I422_10BE, "\
109                 "   I420_12LE, I420_12BE, I420_10LE, I420_10BE, " \
110                 "   I420, YV12, NV12, NV21, Y41B, RGB, BGR, xRGB, xBGR, "\
111                 "   RGBx, BGRx } "
112 #else
113 #define FORMATS " { AYUV, VUYA, BGRA, ARGB, RGBA, ABGR, Y444, Y42B, YUY2, UYVY, "\
114                 "   YVYU, I422_12BE, I422_12LE, I422_10BE, I422_10LE, "\
115                 "   I420_12BE, I420_12LE, I420_10BE, I420_10LE, "\
116                 "   I420, YV12, NV12, NV21, Y41B, RGB, BGR, xRGB, xBGR, "\
117                 "   RGBx, BGRx } "
118 #endif
119
120 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
121     GST_PAD_SRC,
122     GST_PAD_ALWAYS,
123     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (FORMATS))
124     );
125
126 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%u",
127     GST_PAD_SINK,
128     GST_PAD_REQUEST,
129     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL))
130     );
131
132 static void gst_compositor_child_proxy_init (gpointer g_iface,
133     gpointer iface_data);
134
135 #define GST_TYPE_COMPOSITOR_OPERATOR (gst_compositor_operator_get_type())
136 static GType
137 gst_compositor_operator_get_type (void)
138 {
139   static GType compositor_operator_type = 0;
140
141   static const GEnumValue compositor_operator[] = {
142     {COMPOSITOR_OPERATOR_SOURCE, "Source", "source"},
143     {COMPOSITOR_OPERATOR_OVER, "Over", "over"},
144     {COMPOSITOR_OPERATOR_ADD, "Add", "add"},
145     {0, NULL, NULL},
146   };
147
148   if (!compositor_operator_type) {
149     compositor_operator_type =
150         g_enum_register_static ("GstCompositorOperator", compositor_operator);
151   }
152   return compositor_operator_type;
153 }
154
155 #define GST_TYPE_COMPOSITOR_BACKGROUND (gst_compositor_background_get_type())
156 static GType
157 gst_compositor_background_get_type (void)
158 {
159   static GType compositor_background_type = 0;
160
161   static const GEnumValue compositor_background[] = {
162     {COMPOSITOR_BACKGROUND_CHECKER, "Checker pattern", "checker"},
163     {COMPOSITOR_BACKGROUND_BLACK, "Black", "black"},
164     {COMPOSITOR_BACKGROUND_WHITE, "White", "white"},
165     {COMPOSITOR_BACKGROUND_TRANSPARENT,
166         "Transparent Background to enable further compositing", "transparent"},
167     {0, NULL, NULL},
168   };
169
170   if (!compositor_background_type) {
171     compositor_background_type =
172         g_enum_register_static ("GstCompositorBackground",
173         compositor_background);
174   }
175   return compositor_background_type;
176 }
177
178 #define GST_TYPE_COMPOSITOR_SIZING_POLICY (gst_compositor_sizing_policy_get_type())
179 static GType
180 gst_compositor_sizing_policy_get_type (void)
181 {
182   static GType sizing_policy_type = 0;
183
184   static const GEnumValue sizing_polices[] = {
185     {COMPOSITOR_SIZING_POLICY_NONE,
186         "None: Image is scaled to fill configured destination rectangle without "
187           "padding or keeping the aspect ratio", "none"},
188     {COMPOSITOR_SIZING_POLICY_KEEP_ASPECT_RATIO,
189           "Keep Aspect Ratio: Image is scaled to fit destination rectangle "
190           "specified by GstCompositorPad:{xpos, ypos, width, height} "
191           "with preserved aspect ratio. Resulting image will be centered in "
192           "the destination rectangle with padding if necessary",
193         "keep-aspect-ratio"},
194     {0, NULL, NULL},
195   };
196
197   if (!sizing_policy_type) {
198     sizing_policy_type =
199         g_enum_register_static ("GstCompositorSizingPolicy", sizing_polices);
200   }
201   return sizing_policy_type;
202 }
203
204 #define DEFAULT_PAD_XPOS   0
205 #define DEFAULT_PAD_YPOS   0
206 #define DEFAULT_PAD_WIDTH  -1
207 #define DEFAULT_PAD_HEIGHT -1
208 #define DEFAULT_PAD_ALPHA  1.0
209 #define DEFAULT_PAD_OPERATOR COMPOSITOR_OPERATOR_OVER
210 #define DEFAULT_PAD_SIZING_POLICY COMPOSITOR_SIZING_POLICY_NONE
211
212 enum
213 {
214   PROP_PAD_0,
215   PROP_PAD_XPOS,
216   PROP_PAD_YPOS,
217   PROP_PAD_WIDTH,
218   PROP_PAD_HEIGHT,
219   PROP_PAD_ALPHA,
220   PROP_PAD_OPERATOR,
221   PROP_PAD_SIZING_POLICY,
222 };
223
224 G_DEFINE_TYPE (GstCompositorPad, gst_compositor_pad,
225     GST_TYPE_VIDEO_AGGREGATOR_PARALLEL_CONVERT_PAD);
226
227 static void
228 gst_compositor_pad_get_property (GObject * object, guint prop_id,
229     GValue * value, GParamSpec * pspec)
230 {
231   GstCompositorPad *pad = GST_COMPOSITOR_PAD (object);
232
233   switch (prop_id) {
234     case PROP_PAD_XPOS:
235       g_value_set_int (value, pad->xpos);
236       break;
237     case PROP_PAD_YPOS:
238       g_value_set_int (value, pad->ypos);
239       break;
240     case PROP_PAD_WIDTH:
241       g_value_set_int (value, pad->width);
242       break;
243     case PROP_PAD_HEIGHT:
244       g_value_set_int (value, pad->height);
245       break;
246     case PROP_PAD_ALPHA:
247       g_value_set_double (value, pad->alpha);
248       break;
249     case PROP_PAD_OPERATOR:
250       g_value_set_enum (value, pad->op);
251       break;
252     case PROP_PAD_SIZING_POLICY:
253       g_value_set_enum (value, pad->sizing_policy);
254       break;
255     default:
256       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
257       break;
258   }
259 }
260
261 static void
262 gst_compositor_pad_set_property (GObject * object, guint prop_id,
263     const GValue * value, GParamSpec * pspec)
264 {
265   GstCompositorPad *pad = GST_COMPOSITOR_PAD (object);
266
267   switch (prop_id) {
268     case PROP_PAD_XPOS:
269       pad->xpos = g_value_get_int (value);
270       break;
271     case PROP_PAD_YPOS:
272       pad->ypos = g_value_get_int (value);
273       break;
274     case PROP_PAD_WIDTH:
275       pad->width = g_value_get_int (value);
276       gst_video_aggregator_convert_pad_update_conversion_info
277           (GST_VIDEO_AGGREGATOR_CONVERT_PAD (pad));
278       break;
279     case PROP_PAD_HEIGHT:
280       pad->height = g_value_get_int (value);
281       gst_video_aggregator_convert_pad_update_conversion_info
282           (GST_VIDEO_AGGREGATOR_CONVERT_PAD (pad));
283       break;
284     case PROP_PAD_ALPHA:
285       pad->alpha = g_value_get_double (value);
286       break;
287     case PROP_PAD_OPERATOR:
288       pad->op = g_value_get_enum (value);
289       gst_video_aggregator_pad_set_needs_alpha (GST_VIDEO_AGGREGATOR_PAD (pad),
290           pad->op == COMPOSITOR_OPERATOR_ADD);
291       break;
292     case PROP_PAD_SIZING_POLICY:
293       pad->sizing_policy = g_value_get_enum (value);
294       gst_video_aggregator_convert_pad_update_conversion_info
295           (GST_VIDEO_AGGREGATOR_CONVERT_PAD (pad));
296       break;
297     default:
298       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
299       break;
300   }
301 }
302
303 static void
304 _mixer_pad_get_output_size (GstCompositor * comp, GstCompositorPad * comp_pad,
305     gint out_par_n, gint out_par_d, gint * width, gint * height,
306     gint * x_offset, gint * y_offset)
307 {
308   GstVideoAggregatorPad *vagg_pad = GST_VIDEO_AGGREGATOR_PAD (comp_pad);
309   gint pad_width, pad_height;
310   guint dar_n, dar_d;
311
312   *x_offset = 0;
313   *y_offset = 0;
314   *width = 0;
315   *height = 0;
316
317   /* FIXME: Anything better we can do here? */
318   if (!vagg_pad->info.finfo
319       || vagg_pad->info.finfo->format == GST_VIDEO_FORMAT_UNKNOWN) {
320     GST_DEBUG_OBJECT (comp_pad, "Have no caps yet");
321     return;
322   }
323
324   if (comp->zero_size_is_unscaled) {
325     pad_width =
326         comp_pad->width <=
327         0 ? GST_VIDEO_INFO_WIDTH (&vagg_pad->info) : comp_pad->width;
328     pad_height =
329         comp_pad->height <=
330         0 ? GST_VIDEO_INFO_HEIGHT (&vagg_pad->info) : comp_pad->height;
331   } else {
332     pad_width =
333         comp_pad->width <
334         0 ? GST_VIDEO_INFO_WIDTH (&vagg_pad->info) : comp_pad->width;
335     pad_height =
336         comp_pad->height <
337         0 ? GST_VIDEO_INFO_HEIGHT (&vagg_pad->info) : comp_pad->height;
338   }
339
340   if (pad_width == 0 || pad_height == 0)
341     return;
342
343   if (!gst_video_calculate_display_ratio (&dar_n, &dar_d, pad_width, pad_height,
344           GST_VIDEO_INFO_PAR_N (&vagg_pad->info),
345           GST_VIDEO_INFO_PAR_D (&vagg_pad->info), out_par_n, out_par_d)) {
346     GST_WARNING_OBJECT (comp_pad, "Cannot calculate display aspect ratio");
347     return;
348   }
349
350   GST_LOG_OBJECT (comp_pad, "scaling %ux%u by %u/%u (%u/%u / %u/%u)", pad_width,
351       pad_height, dar_n, dar_d, GST_VIDEO_INFO_PAR_N (&vagg_pad->info),
352       GST_VIDEO_INFO_PAR_D (&vagg_pad->info), out_par_n, out_par_d);
353
354   switch (comp_pad->sizing_policy) {
355     case COMPOSITOR_SIZING_POLICY_NONE:
356       /* Pick either height or width, whichever is an integer multiple of the
357        * display aspect ratio. However, prefer preserving the height to account
358        * for interlaced video. */
359       if (pad_height % dar_n == 0) {
360         pad_width = gst_util_uint64_scale_int (pad_height, dar_n, dar_d);
361       } else if (pad_width % dar_d == 0) {
362         pad_height = gst_util_uint64_scale_int (pad_width, dar_d, dar_n);
363       } else {
364         pad_width = gst_util_uint64_scale_int (pad_height, dar_n, dar_d);
365       }
366       break;
367     case COMPOSITOR_SIZING_POLICY_KEEP_ASPECT_RATIO:
368     {
369       gint from_dar_n, from_dar_d, to_dar_n, to_dar_d, num, den;
370
371       /* Calculate DAR again with actual video size */
372       if (!gst_util_fraction_multiply (GST_VIDEO_INFO_WIDTH (&vagg_pad->info),
373               GST_VIDEO_INFO_HEIGHT (&vagg_pad->info),
374               GST_VIDEO_INFO_PAR_N (&vagg_pad->info),
375               GST_VIDEO_INFO_PAR_D (&vagg_pad->info), &from_dar_n,
376               &from_dar_d)) {
377         from_dar_n = from_dar_d = -1;
378       }
379
380       if (!gst_util_fraction_multiply (pad_width, pad_height,
381               out_par_n, out_par_d, &to_dar_n, &to_dar_d)) {
382         to_dar_n = to_dar_d = -1;
383       }
384
385       if (from_dar_n != to_dar_n || from_dar_d != to_dar_d) {
386         /* Calculate new output resolution */
387         if (from_dar_n != -1 && from_dar_d != -1
388             && gst_util_fraction_multiply (from_dar_n, from_dar_d,
389                 out_par_d, out_par_n, &num, &den)) {
390           GstVideoRectangle src_rect, dst_rect, rst_rect;
391
392           src_rect.h = gst_util_uint64_scale_int (pad_width, den, num);
393           if (src_rect.h == 0) {
394             pad_width = 0;
395             pad_height = 0;
396             break;
397           }
398
399           src_rect.x = src_rect.y = 0;
400           src_rect.w = pad_width;
401
402           dst_rect.x = dst_rect.y = 0;
403           dst_rect.w = pad_width;
404           dst_rect.h = pad_height;
405
406           /* Scale rect to be centered in destination rect */
407           gst_video_center_rect (&src_rect, &dst_rect, &rst_rect, TRUE);
408
409           GST_LOG_OBJECT (comp_pad,
410               "Re-calculated size %dx%d -> %dx%d (x-offset %d, y-offset %d)",
411               pad_width, pad_height, rst_rect.w, rst_rect.h, rst_rect.x,
412               rst_rect.h);
413
414           *x_offset = rst_rect.x;
415           *y_offset = rst_rect.y;
416           pad_width = rst_rect.w;
417           pad_height = rst_rect.h;
418         } else {
419           GST_WARNING_OBJECT (comp_pad, "Failed to calculate output size");
420
421           *x_offset = 0;
422           *y_offset = 0;
423           pad_width = 0;
424           pad_height = 0;
425         }
426       }
427       break;
428     }
429   }
430
431   *width = pad_width;
432   *height = pad_height;
433 }
434
435 static gboolean
436 is_point_contained (const GstVideoRectangle rect, const gint px, const gint py)
437 {
438   if ((px >= rect.x) && (px <= rect.x + rect.w) &&
439       (py >= rect.y) && (py <= rect.y + rect.h))
440     return TRUE;
441   return FALSE;
442 }
443
444 /* Test whether rectangle2 contains rectangle 1 (geometrically) */
445 static gboolean
446 is_rectangle_contained (const GstVideoRectangle rect1,
447     const GstVideoRectangle rect2)
448 {
449   if ((rect2.x <= rect1.x) && (rect2.y <= rect1.y) &&
450       ((rect2.x + rect2.w) >= (rect1.x + rect1.w)) &&
451       ((rect2.y + rect2.h) >= (rect1.y + rect1.h)))
452     return TRUE;
453   return FALSE;
454 }
455
456 static GstVideoRectangle
457 clamp_rectangle (gint x, gint y, gint w, gint h, gint outer_width,
458     gint outer_height)
459 {
460   gint x2 = x + w;
461   gint y2 = y + h;
462   GstVideoRectangle clamped;
463
464   /* Clamp the x/y coordinates of this frame to the output boundaries to cover
465    * the case where (say, with negative xpos/ypos or w/h greater than the output
466    * size) the non-obscured portion of the frame could be outside the bounds of
467    * the video itself and hence not visible at all */
468   clamped.x = CLAMP (x, 0, outer_width);
469   clamped.y = CLAMP (y, 0, outer_height);
470   clamped.w = CLAMP (x2, 0, outer_width) - clamped.x;
471   clamped.h = CLAMP (y2, 0, outer_height) - clamped.y;
472
473   return clamped;
474 }
475
476 /* Call this with the lock taken */
477 static gboolean
478 _pad_obscures_rectangle (GstVideoAggregator * vagg, GstVideoAggregatorPad * pad,
479     const GstVideoRectangle rect)
480 {
481   GstVideoRectangle pad_rect;
482   GstCompositorPad *cpad = GST_COMPOSITOR_PAD (pad);
483   GstStructure *converter_config = NULL;
484   gboolean fill_border = TRUE;
485   guint32 border_argb = 0xff000000;
486   gint x_offset, y_offset;
487
488   /* No buffer to obscure the rectangle with */
489   if (!gst_video_aggregator_pad_has_current_buffer (pad))
490     return FALSE;
491
492   /* Can't obscure if we introduce alpha or if the format has an alpha
493    * component as we'd have to inspect every pixel to know if the frame is
494    * opaque, so assume it doesn't obscure
495    */
496   if (cpad->alpha != 1.0 || GST_VIDEO_INFO_HAS_ALPHA (&pad->info))
497     return FALSE;
498
499   /* If a converter-config is set and it is either configured to not fill any
500    * borders, or configured to use a non-opaque color, then we have to handle
501    * the pad as potentially containing transparency */
502   g_object_get (pad, "converter-config", &converter_config, NULL);
503   if (converter_config) {
504     gst_structure_get (converter_config, GST_VIDEO_CONVERTER_OPT_BORDER_ARGB,
505         G_TYPE_UINT, &border_argb, NULL);
506     gst_structure_get (converter_config, GST_VIDEO_CONVERTER_OPT_FILL_BORDER,
507         G_TYPE_BOOLEAN, &fill_border, NULL);
508   }
509   gst_clear_structure (&converter_config);
510   if (!fill_border || (border_argb & 0xff000000) != 0xff000000)
511     return FALSE;
512
513   pad_rect.x = cpad->xpos;
514   pad_rect.y = cpad->ypos;
515   /* Handle pixel and display aspect ratios to find the actual size */
516   _mixer_pad_get_output_size (GST_COMPOSITOR (vagg), cpad,
517       GST_VIDEO_INFO_PAR_N (&vagg->info), GST_VIDEO_INFO_PAR_D (&vagg->info),
518       &(pad_rect.w), &(pad_rect.h), &x_offset, &y_offset);
519   pad_rect.x += x_offset;
520   pad_rect.y += y_offset;
521
522   if (!is_rectangle_contained (rect, pad_rect))
523     return FALSE;
524
525   GST_DEBUG_OBJECT (pad, "Pad %s %ix%i@(%i,%i) obscures rect %ix%i@(%i,%i)",
526       GST_PAD_NAME (pad), pad_rect.w, pad_rect.h, pad_rect.x, pad_rect.y,
527       rect.w, rect.h, rect.x, rect.y);
528
529   return TRUE;
530 }
531
532 static void
533 gst_compositor_pad_prepare_frame_start (GstVideoAggregatorPad * pad,
534     GstVideoAggregator * vagg, GstBuffer * buffer,
535     GstVideoFrame * prepared_frame)
536 {
537   GstCompositorPad *cpad = GST_COMPOSITOR_PAD (pad);
538   gint width, height;
539   gboolean frame_obscured = FALSE;
540   GList *l;
541   /* The rectangle representing this frame, clamped to the video's boundaries.
542    * Due to the clamping, this is different from the frame width/height above. */
543   GstVideoRectangle frame_rect;
544
545   /* There's three types of width/height here:
546    * 1. GST_VIDEO_FRAME_WIDTH/HEIGHT:
547    *     The frame width/height (same as pad->info.height/width;
548    *     see gst_video_frame_map())
549    * 2. cpad->width/height:
550    *     The optional pad property for scaling the frame (if zero, the video is
551    *     left unscaled)
552    * 3. conversion_info.width/height:
553    *     Equal to cpad->width/height if it's set, otherwise it's the pad
554    *     width/height. See ->set_info()
555    * */
556
557   _mixer_pad_get_output_size (GST_COMPOSITOR (vagg), cpad,
558       GST_VIDEO_INFO_PAR_N (&vagg->info), GST_VIDEO_INFO_PAR_D (&vagg->info),
559       &width, &height, &cpad->x_offset, &cpad->y_offset);
560
561   if (cpad->alpha == 0.0) {
562     GST_DEBUG_OBJECT (pad, "Pad has alpha 0.0, not converting frame");
563     return;
564   }
565
566   if (gst_aggregator_pad_is_inactive (GST_AGGREGATOR_PAD (pad)))
567     return;
568
569   frame_rect = clamp_rectangle (cpad->xpos + cpad->x_offset,
570       cpad->ypos + cpad->y_offset, width, height,
571       GST_VIDEO_INFO_WIDTH (&vagg->info), GST_VIDEO_INFO_HEIGHT (&vagg->info));
572
573   if (frame_rect.w == 0 || frame_rect.h == 0) {
574     GST_DEBUG_OBJECT (pad, "Resulting frame is zero-width or zero-height "
575         "(w: %i, h: %i), skipping", frame_rect.w, frame_rect.h);
576     return;
577   }
578
579   GST_OBJECT_LOCK (vagg);
580   /* Check if this frame is obscured by a higher-zorder frame
581    * TODO: Also skip a frame if it's obscured by a combination of
582    * higher-zorder frames */
583   l = g_list_find (GST_ELEMENT (vagg)->sinkpads, pad);
584   /* The pad might've just been removed */
585   if (l)
586     l = l->next;
587   for (; l; l = l->next) {
588     GstBuffer *pad_buffer;
589
590     pad_buffer =
591         gst_video_aggregator_pad_get_current_buffer (GST_VIDEO_AGGREGATOR_PAD
592         (l->data));
593
594     if (pad_buffer == NULL)
595       continue;
596
597     if (gst_buffer_get_size (pad_buffer) == 0 &&
598         GST_BUFFER_FLAG_IS_SET (pad_buffer, GST_BUFFER_FLAG_GAP)) {
599       continue;
600     }
601
602     if (_pad_obscures_rectangle (vagg, l->data, frame_rect)) {
603       frame_obscured = TRUE;
604       break;
605     }
606   }
607   GST_OBJECT_UNLOCK (vagg);
608
609   if (frame_obscured)
610     return;
611
612   GST_VIDEO_AGGREGATOR_PAD_CLASS
613       (gst_compositor_pad_parent_class)->prepare_frame_start (pad, vagg, buffer,
614       prepared_frame);
615 }
616
617 static void
618 gst_compositor_pad_create_conversion_info (GstVideoAggregatorConvertPad * pad,
619     GstVideoAggregator * vagg, GstVideoInfo * conversion_info)
620 {
621   GstCompositorPad *cpad = GST_COMPOSITOR_PAD (pad);
622   gint width, height;
623   gint x_offset, y_offset;
624
625   GST_VIDEO_AGGREGATOR_CONVERT_PAD_CLASS
626       (gst_compositor_pad_parent_class)->create_conversion_info (pad, vagg,
627       conversion_info);
628   if (!conversion_info->finfo)
629     return;
630
631   _mixer_pad_get_output_size (GST_COMPOSITOR (vagg), cpad,
632       GST_VIDEO_INFO_PAR_N (&vagg->info), GST_VIDEO_INFO_PAR_D (&vagg->info),
633       &width, &height, &x_offset, &y_offset);
634
635   /* The only thing that can change here is the width
636    * and height, otherwise set_info would've been called */
637   if (GST_VIDEO_INFO_WIDTH (conversion_info) != width ||
638       GST_VIDEO_INFO_HEIGHT (conversion_info) != height) {
639     GstVideoInfo tmp_info;
640
641     /* Initialize with the wanted video format and our original width and
642      * height as we don't want to rescale. Then copy over the wanted
643      * colorimetry, and chroma-site and our current pixel-aspect-ratio
644      * and other relevant fields.
645      */
646     gst_video_info_set_format (&tmp_info,
647         GST_VIDEO_INFO_FORMAT (conversion_info), width, height);
648     tmp_info.chroma_site = conversion_info->chroma_site;
649     tmp_info.colorimetry = conversion_info->colorimetry;
650     tmp_info.par_n = conversion_info->par_n;
651     tmp_info.par_d = conversion_info->par_d;
652     tmp_info.fps_n = conversion_info->fps_n;
653     tmp_info.fps_d = conversion_info->fps_d;
654     tmp_info.flags = conversion_info->flags;
655     tmp_info.interlace_mode = conversion_info->interlace_mode;
656
657     *conversion_info = tmp_info;
658   }
659 }
660
661 static void
662 gst_compositor_pad_class_init (GstCompositorPadClass * klass)
663 {
664   GObjectClass *gobject_class = (GObjectClass *) klass;
665   GstVideoAggregatorPadClass *vaggpadclass =
666       (GstVideoAggregatorPadClass *) klass;
667   GstVideoAggregatorConvertPadClass *vaggcpadclass =
668       (GstVideoAggregatorConvertPadClass *) klass;
669
670   gobject_class->set_property = gst_compositor_pad_set_property;
671   gobject_class->get_property = gst_compositor_pad_get_property;
672
673   g_object_class_install_property (gobject_class, PROP_PAD_XPOS,
674       g_param_spec_int ("xpos", "X Position", "X Position of the picture",
675           G_MININT, G_MAXINT, DEFAULT_PAD_XPOS,
676           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
677   g_object_class_install_property (gobject_class, PROP_PAD_YPOS,
678       g_param_spec_int ("ypos", "Y Position", "Y Position of the picture",
679           G_MININT, G_MAXINT, DEFAULT_PAD_YPOS,
680           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
681   g_object_class_install_property (gobject_class, PROP_PAD_WIDTH,
682       g_param_spec_int ("width", "Width", "Width of the picture",
683           G_MININT, G_MAXINT, DEFAULT_PAD_WIDTH,
684           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
685   g_object_class_install_property (gobject_class, PROP_PAD_HEIGHT,
686       g_param_spec_int ("height", "Height", "Height of the picture",
687           G_MININT, G_MAXINT, DEFAULT_PAD_HEIGHT,
688           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
689   g_object_class_install_property (gobject_class, PROP_PAD_ALPHA,
690       g_param_spec_double ("alpha", "Alpha", "Alpha of the picture", 0.0, 1.0,
691           DEFAULT_PAD_ALPHA,
692           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
693   g_object_class_install_property (gobject_class, PROP_PAD_OPERATOR,
694       g_param_spec_enum ("operator", "Operator",
695           "Blending operator to use for blending this pad over the previous ones",
696           GST_TYPE_COMPOSITOR_OPERATOR, DEFAULT_PAD_OPERATOR,
697           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
698
699   /**
700    * GstCompositorPad:sizing-policy:
701    *
702    * Specifies sizing policy to use. Depending on selected sizing policy,
703    * scaled image might not fully cover the configured target rectangle area
704    * (e.g., "keep-aspect-ratio"). In that case, any uncovered area will be
705    * filled with background unless the uncovered area is drawn by other image.
706    *
707    * Since: 1.20
708    */
709   g_object_class_install_property (gobject_class, PROP_PAD_SIZING_POLICY,
710       g_param_spec_enum ("sizing-policy", "Sizing policy",
711           "Sizing policy to use for image scaling",
712           GST_TYPE_COMPOSITOR_SIZING_POLICY, DEFAULT_PAD_SIZING_POLICY,
713           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
714
715   vaggpadclass->prepare_frame_start =
716       GST_DEBUG_FUNCPTR (gst_compositor_pad_prepare_frame_start);
717
718   vaggcpadclass->create_conversion_info =
719       GST_DEBUG_FUNCPTR (gst_compositor_pad_create_conversion_info);
720
721   gst_type_mark_as_plugin_api (GST_TYPE_COMPOSITOR_SIZING_POLICY, 0);
722 }
723
724 static void
725 gst_compositor_pad_init (GstCompositorPad * compo_pad)
726 {
727   compo_pad->xpos = DEFAULT_PAD_XPOS;
728   compo_pad->ypos = DEFAULT_PAD_YPOS;
729   compo_pad->alpha = DEFAULT_PAD_ALPHA;
730   compo_pad->op = DEFAULT_PAD_OPERATOR;
731   compo_pad->width = DEFAULT_PAD_WIDTH;
732   compo_pad->height = DEFAULT_PAD_HEIGHT;
733   compo_pad->sizing_policy = DEFAULT_PAD_SIZING_POLICY;
734 }
735
736
737 /* GstCompositor */
738 #define DEFAULT_BACKGROUND COMPOSITOR_BACKGROUND_CHECKER
739 #define DEFAULT_ZERO_SIZE_IS_UNSCALED TRUE
740 #define DEFAULT_MAX_THREADS 0
741
742 enum
743 {
744   PROP_0,
745   PROP_BACKGROUND,
746   PROP_ZERO_SIZE_IS_UNSCALED,
747   PROP_MAX_THREADS,
748   PROP_IGNORE_INACTIVE_PADS,
749 };
750
751 static void
752 gst_compositor_get_property (GObject * object,
753     guint prop_id, GValue * value, GParamSpec * pspec)
754 {
755   GstCompositor *self = GST_COMPOSITOR (object);
756
757   switch (prop_id) {
758     case PROP_BACKGROUND:
759       g_value_set_enum (value, self->background);
760       break;
761     case PROP_ZERO_SIZE_IS_UNSCALED:
762       g_value_set_boolean (value, self->zero_size_is_unscaled);
763       break;
764     case PROP_MAX_THREADS:
765       g_value_set_uint (value, self->max_threads);
766       break;
767     case PROP_IGNORE_INACTIVE_PADS:
768       g_value_set_boolean (value,
769           gst_aggregator_get_ignore_inactive_pads (GST_AGGREGATOR (object)));
770       break;
771     default:
772       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
773       break;
774   }
775 }
776
777 static void
778 gst_compositor_set_property (GObject * object,
779     guint prop_id, const GValue * value, GParamSpec * pspec)
780 {
781   GstCompositor *self = GST_COMPOSITOR (object);
782
783   switch (prop_id) {
784     case PROP_BACKGROUND:
785       self->background = g_value_get_enum (value);
786       break;
787     case PROP_ZERO_SIZE_IS_UNSCALED:
788       self->zero_size_is_unscaled = g_value_get_boolean (value);
789       break;
790     case PROP_MAX_THREADS:
791       self->max_threads = g_value_get_uint (value);
792       break;
793     case PROP_IGNORE_INACTIVE_PADS:
794       gst_aggregator_set_ignore_inactive_pads (GST_AGGREGATOR (object),
795           g_value_get_boolean (value));
796       break;
797     default:
798       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
799       break;
800   }
801 }
802
803 #define gst_compositor_parent_class parent_class
804 G_DEFINE_TYPE_WITH_CODE (GstCompositor, gst_compositor,
805     GST_TYPE_VIDEO_AGGREGATOR, G_IMPLEMENT_INTERFACE (GST_TYPE_CHILD_PROXY,
806         gst_compositor_child_proxy_init));
807 GST_ELEMENT_REGISTER_DEFINE (compositor, "compositor", GST_RANK_PRIMARY + 1,
808     GST_TYPE_COMPOSITOR);
809
810 static gboolean
811 set_functions (GstCompositor * self, const GstVideoInfo * info)
812 {
813   gint offset[GST_VIDEO_MAX_COMPONENTS] = { 0, };
814   gint scale[GST_VIDEO_MAX_COMPONENTS] = { 0, };
815   gint i;
816
817   self->blend = NULL;
818   self->overlay = NULL;
819   self->fill_checker = NULL;
820   self->fill_color = NULL;
821
822   switch (GST_VIDEO_INFO_FORMAT (info)) {
823     case GST_VIDEO_FORMAT_AYUV:
824       self->blend = gst_compositor_blend_ayuv;
825       self->overlay = gst_compositor_overlay_ayuv;
826       self->fill_checker = gst_compositor_fill_checker_ayuv;
827       self->fill_color = gst_compositor_fill_color_ayuv;
828       break;
829     case GST_VIDEO_FORMAT_VUYA:
830       self->blend = gst_compositor_blend_vuya;
831       self->overlay = gst_compositor_overlay_vuya;
832       self->fill_checker = gst_compositor_fill_checker_vuya;
833       self->fill_color = gst_compositor_fill_color_vuya;
834       break;
835     case GST_VIDEO_FORMAT_ARGB:
836       self->blend = gst_compositor_blend_argb;
837       self->overlay = gst_compositor_overlay_argb;
838       self->fill_checker = gst_compositor_fill_checker_argb;
839       self->fill_color = gst_compositor_fill_color_argb;
840       break;
841     case GST_VIDEO_FORMAT_BGRA:
842       self->blend = gst_compositor_blend_bgra;
843       self->overlay = gst_compositor_overlay_bgra;
844       self->fill_checker = gst_compositor_fill_checker_bgra;
845       self->fill_color = gst_compositor_fill_color_bgra;
846       break;
847     case GST_VIDEO_FORMAT_ABGR:
848       self->blend = gst_compositor_blend_abgr;
849       self->overlay = gst_compositor_overlay_abgr;
850       self->fill_checker = gst_compositor_fill_checker_abgr;
851       self->fill_color = gst_compositor_fill_color_abgr;
852       break;
853     case GST_VIDEO_FORMAT_RGBA:
854       self->blend = gst_compositor_blend_rgba;
855       self->overlay = gst_compositor_overlay_rgba;
856       self->fill_checker = gst_compositor_fill_checker_rgba;
857       self->fill_color = gst_compositor_fill_color_rgba;
858       break;
859     case GST_VIDEO_FORMAT_Y444:
860       self->blend = gst_compositor_blend_y444;
861       self->overlay = self->blend;
862       self->fill_checker = gst_compositor_fill_checker_y444;
863       self->fill_color = gst_compositor_fill_color_y444;
864       break;
865     case GST_VIDEO_FORMAT_Y42B:
866       self->blend = gst_compositor_blend_y42b;
867       self->overlay = self->blend;
868       self->fill_checker = gst_compositor_fill_checker_y42b;
869       self->fill_color = gst_compositor_fill_color_y42b;
870       break;
871     case GST_VIDEO_FORMAT_YUY2:
872       self->blend = gst_compositor_blend_yuy2;
873       self->overlay = self->blend;
874       self->fill_checker = gst_compositor_fill_checker_yuy2;
875       self->fill_color = gst_compositor_fill_color_yuy2;
876       break;
877     case GST_VIDEO_FORMAT_UYVY:
878       self->blend = gst_compositor_blend_uyvy;
879       self->overlay = self->blend;
880       self->fill_checker = gst_compositor_fill_checker_uyvy;
881       self->fill_color = gst_compositor_fill_color_uyvy;
882       break;
883     case GST_VIDEO_FORMAT_YVYU:
884       self->blend = gst_compositor_blend_yvyu;
885       self->overlay = self->blend;
886       self->fill_checker = gst_compositor_fill_checker_yvyu;
887       self->fill_color = gst_compositor_fill_color_yvyu;
888       break;
889     case GST_VIDEO_FORMAT_I422_12LE:
890       self->blend = gst_compositor_blend_i422_12le;
891       self->overlay = self->blend;
892       self->fill_checker = gst_compositor_fill_checker_i422_12le;
893       self->fill_color = gst_compositor_fill_color_i422_12le;
894       break;
895     case GST_VIDEO_FORMAT_I422_12BE:
896       self->blend = gst_compositor_blend_i422_12be;
897       self->overlay = self->blend;
898       self->fill_checker = gst_compositor_fill_checker_i422_12be;
899       self->fill_color = gst_compositor_fill_color_i422_12be;
900       break;
901     case GST_VIDEO_FORMAT_I422_10LE:
902       self->blend = gst_compositor_blend_i422_10le;
903       self->overlay = self->blend;
904       self->fill_checker = gst_compositor_fill_checker_i422_10le;
905       self->fill_color = gst_compositor_fill_color_i422_10le;
906       break;
907     case GST_VIDEO_FORMAT_I422_10BE:
908       self->blend = gst_compositor_blend_i422_10be;
909       self->overlay = self->blend;
910       self->fill_checker = gst_compositor_fill_checker_i422_10be;
911       self->fill_color = gst_compositor_fill_color_i422_10be;
912       break;
913     case GST_VIDEO_FORMAT_I420_12LE:
914       self->blend = gst_compositor_blend_i420_12le;
915       self->overlay = self->blend;
916       self->fill_checker = gst_compositor_fill_checker_i420_12le;
917       self->fill_color = gst_compositor_fill_color_i420_12le;
918       break;
919     case GST_VIDEO_FORMAT_I420_12BE:
920       self->blend = gst_compositor_blend_i420_12be;
921       self->overlay = self->blend;
922       self->fill_checker = gst_compositor_fill_checker_i420_12be;
923       self->fill_color = gst_compositor_fill_color_i420_12be;
924       break;
925     case GST_VIDEO_FORMAT_I420_10LE:
926       self->blend = gst_compositor_blend_i420_10le;
927       self->overlay = self->blend;
928       self->fill_checker = gst_compositor_fill_checker_i420_10le;
929       self->fill_color = gst_compositor_fill_color_i420_10le;
930       break;
931     case GST_VIDEO_FORMAT_I420_10BE:
932       self->blend = gst_compositor_blend_i420_10be;
933       self->overlay = self->blend;
934       self->fill_checker = gst_compositor_fill_checker_i420_10be;
935       self->fill_color = gst_compositor_fill_color_i420_10be;
936       break;
937     case GST_VIDEO_FORMAT_I420:
938       self->blend = gst_compositor_blend_i420;
939       self->overlay = self->blend;
940       self->fill_checker = gst_compositor_fill_checker_i420;
941       self->fill_color = gst_compositor_fill_color_i420;
942       break;
943     case GST_VIDEO_FORMAT_YV12:
944       self->blend = gst_compositor_blend_yv12;
945       self->overlay = self->blend;
946       self->fill_checker = gst_compositor_fill_checker_yv12;
947       self->fill_color = gst_compositor_fill_color_yv12;
948       break;
949     case GST_VIDEO_FORMAT_NV12:
950       self->blend = gst_compositor_blend_nv12;
951       self->overlay = self->blend;
952       self->fill_checker = gst_compositor_fill_checker_nv12;
953       self->fill_color = gst_compositor_fill_color_nv12;
954       break;
955     case GST_VIDEO_FORMAT_NV21:
956       self->blend = gst_compositor_blend_nv21;
957       self->overlay = self->blend;
958       self->fill_checker = gst_compositor_fill_checker_nv21;
959       self->fill_color = gst_compositor_fill_color_nv21;
960       break;
961     case GST_VIDEO_FORMAT_Y41B:
962       self->blend = gst_compositor_blend_y41b;
963       self->overlay = self->blend;
964       self->fill_checker = gst_compositor_fill_checker_y41b;
965       self->fill_color = gst_compositor_fill_color_y41b;
966       break;
967     case GST_VIDEO_FORMAT_RGB:
968       self->blend = gst_compositor_blend_rgb;
969       self->overlay = self->blend;
970       self->fill_checker = gst_compositor_fill_checker_rgb;
971       self->fill_color = gst_compositor_fill_color_rgb;
972       break;
973     case GST_VIDEO_FORMAT_BGR:
974       self->blend = gst_compositor_blend_bgr;
975       self->overlay = self->blend;
976       self->fill_checker = gst_compositor_fill_checker_bgr;
977       self->fill_color = gst_compositor_fill_color_bgr;
978       break;
979     case GST_VIDEO_FORMAT_xRGB:
980       self->blend = gst_compositor_blend_xrgb;
981       self->overlay = self->blend;
982       self->fill_checker = gst_compositor_fill_checker_xrgb;
983       self->fill_color = gst_compositor_fill_color_xrgb;
984       break;
985     case GST_VIDEO_FORMAT_xBGR:
986       self->blend = gst_compositor_blend_xbgr;
987       self->overlay = self->blend;
988       self->fill_checker = gst_compositor_fill_checker_xbgr;
989       self->fill_color = gst_compositor_fill_color_xbgr;
990       break;
991     case GST_VIDEO_FORMAT_RGBx:
992       self->blend = gst_compositor_blend_rgbx;
993       self->overlay = self->blend;
994       self->fill_checker = gst_compositor_fill_checker_rgbx;
995       self->fill_color = gst_compositor_fill_color_rgbx;
996       break;
997     case GST_VIDEO_FORMAT_BGRx:
998       self->blend = gst_compositor_blend_bgrx;
999       self->overlay = self->blend;
1000       self->fill_checker = gst_compositor_fill_checker_bgrx;
1001       self->fill_color = gst_compositor_fill_color_bgrx;
1002       break;
1003     default:
1004       GST_ERROR_OBJECT (self, "Unhandled format %s",
1005           gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (info)));
1006       return FALSE;
1007   }
1008
1009   /* calculate black and white colors */
1010   gst_video_color_range_offsets (info->colorimetry.range, info->finfo,
1011       offset, scale);
1012   if (GST_VIDEO_INFO_IS_YUV (info)) {
1013     /* black color [0.0, 0.0, 0.0] */
1014     self->black_color[0] = offset[0];
1015
1016     /* white color [1.0, 0.0, 0.0] */
1017     self->white_color[0] = scale[0] + offset[0];
1018
1019     for (i = 1; i < 3; i++)
1020       self->black_color[i] = self->white_color[i] = offset[i];
1021   } else {
1022     for (i = 0; i < 3; i++) {
1023       self->black_color[i] = offset[i];
1024       self->white_color[i] = scale[i] + offset[i];
1025     }
1026   }
1027
1028   GST_DEBUG_OBJECT (self,
1029       "Calculated background color block: [%d %d %d], white: [%d %d %d]",
1030       self->black_color[0], self->black_color[1], self->black_color[2],
1031       self->white_color[0], self->white_color[1], self->white_color[2]);
1032
1033   return TRUE;
1034 }
1035
1036 static GstCaps *
1037 _fixate_caps (GstAggregator * agg, GstCaps * caps)
1038 {
1039   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
1040   GList *l;
1041   gint best_width = -1, best_height = -1;
1042   gint best_fps_n = -1, best_fps_d = -1;
1043   gint par_n, par_d;
1044   gdouble best_fps = 0.;
1045   GstCaps *ret = NULL;
1046   GstStructure *s;
1047
1048   ret = gst_caps_make_writable (caps);
1049
1050   /* we need this to calculate how large to make the output frame */
1051   s = gst_caps_get_structure (ret, 0);
1052   if (gst_structure_has_field (s, "pixel-aspect-ratio")) {
1053     gst_structure_fixate_field_nearest_fraction (s, "pixel-aspect-ratio", 1, 1);
1054     gst_structure_get_fraction (s, "pixel-aspect-ratio", &par_n, &par_d);
1055   } else {
1056     par_n = par_d = 1;
1057   }
1058
1059   GST_OBJECT_LOCK (vagg);
1060   for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
1061     GstVideoAggregatorPad *vaggpad = l->data;
1062     GstCompositorPad *compositor_pad = GST_COMPOSITOR_PAD (vaggpad);
1063     gint this_width, this_height;
1064     gint width, height;
1065     gint fps_n, fps_d;
1066     gdouble cur_fps;
1067     gint x_offset;
1068     gint y_offset;
1069
1070     if (gst_aggregator_pad_is_inactive (GST_AGGREGATOR_PAD (vaggpad)))
1071       continue;
1072
1073     fps_n = GST_VIDEO_INFO_FPS_N (&vaggpad->info);
1074     fps_d = GST_VIDEO_INFO_FPS_D (&vaggpad->info);
1075     _mixer_pad_get_output_size (GST_COMPOSITOR (vagg), compositor_pad, par_n,
1076         par_d, &width, &height, &x_offset, &y_offset);
1077
1078     if (width == 0 || height == 0)
1079       continue;
1080
1081     /* {x,y}_offset represent padding size of each top and left area.
1082      * To calculate total resolution, count bottom and right padding area
1083      * as well here */
1084     this_width = width + MAX (compositor_pad->xpos + 2 * x_offset, 0);
1085     this_height = height + MAX (compositor_pad->ypos + 2 * y_offset, 0);
1086
1087     if (best_width < this_width)
1088       best_width = this_width;
1089     if (best_height < this_height)
1090       best_height = this_height;
1091
1092     if (fps_d == 0)
1093       cur_fps = 0.0;
1094     else
1095       gst_util_fraction_to_double (fps_n, fps_d, &cur_fps);
1096
1097     if (best_fps < cur_fps) {
1098       best_fps = cur_fps;
1099       best_fps_n = fps_n;
1100       best_fps_d = fps_d;
1101     }
1102   }
1103   GST_OBJECT_UNLOCK (vagg);
1104
1105   if (best_fps_n <= 0 || best_fps_d <= 0 || best_fps == 0.0) {
1106     best_fps_n = 25;
1107     best_fps_d = 1;
1108     best_fps = 25.0;
1109   }
1110
1111   gst_structure_fixate_field_nearest_int (s, "width", best_width);
1112   gst_structure_fixate_field_nearest_int (s, "height", best_height);
1113   gst_structure_fixate_field_nearest_fraction (s, "framerate", best_fps_n,
1114       best_fps_d);
1115   ret = gst_caps_fixate (ret);
1116
1117   return ret;
1118 }
1119
1120 static void
1121 gst_parallelized_task_thread_func (gpointer data)
1122 {
1123   GstParallelizedTaskRunner *runner = data;
1124   gint idx;
1125
1126   g_mutex_lock (&runner->lock);
1127   idx = runner->n_todo--;
1128   g_assert (runner->n_todo >= -1);
1129   g_mutex_unlock (&runner->lock);
1130
1131   g_assert (runner->func != NULL);
1132
1133   runner->func (runner->task_data[idx]);
1134 }
1135
1136 static void
1137 gst_parallelized_task_runner_join (GstParallelizedTaskRunner * self)
1138 {
1139   gboolean joined = FALSE;
1140
1141   while (!joined) {
1142     g_mutex_lock (&self->lock);
1143     if (!(joined = gst_queue_array_is_empty (self->tasks))) {
1144       gpointer task = gst_queue_array_pop_head (self->tasks);
1145       g_mutex_unlock (&self->lock);
1146       gst_task_pool_join (self->pool, task);
1147     } else {
1148       g_mutex_unlock (&self->lock);
1149     }
1150   }
1151 }
1152
1153 static void
1154 gst_parallelized_task_runner_free (GstParallelizedTaskRunner * self)
1155 {
1156   gst_parallelized_task_runner_join (self);
1157
1158   gst_queue_array_free (self->tasks);
1159   if (self->own_pool)
1160     gst_task_pool_cleanup (self->pool);
1161   gst_object_unref (self->pool);
1162   g_mutex_clear (&self->lock);
1163   g_free (self);
1164 }
1165
1166 static GstParallelizedTaskRunner *
1167 gst_parallelized_task_runner_new (guint n_threads, GstTaskPool * pool,
1168     gboolean async_tasks)
1169 {
1170   GstParallelizedTaskRunner *self;
1171
1172   if (n_threads == 0)
1173     n_threads = g_get_num_processors ();
1174
1175   self = g_new0 (GstParallelizedTaskRunner, 1);
1176
1177   if (pool) {
1178     self->pool = g_object_ref (pool);
1179     self->own_pool = FALSE;
1180
1181     /* No reason to split up the work between more threads than the
1182      * pool can spawn */
1183     if (GST_IS_SHARED_TASK_POOL (pool))
1184       n_threads =
1185           MIN (n_threads,
1186           gst_shared_task_pool_get_max_threads (GST_SHARED_TASK_POOL (pool)));
1187   } else {
1188     self->pool = gst_shared_task_pool_new ();
1189     self->own_pool = TRUE;
1190     gst_shared_task_pool_set_max_threads (GST_SHARED_TASK_POOL (self->pool),
1191         n_threads);
1192     gst_task_pool_prepare (self->pool, NULL);
1193   }
1194
1195   self->tasks = gst_queue_array_new (n_threads);
1196
1197   self->n_threads = n_threads;
1198
1199   self->n_todo = -1;
1200   g_mutex_init (&self->lock);
1201
1202   /* Set when scheduling a job */
1203   self->func = NULL;
1204   self->task_data = NULL;
1205   self->async_tasks = async_tasks;
1206
1207   return self;
1208 }
1209
1210 static void
1211 gst_parallelized_task_runner_finish (GstParallelizedTaskRunner * self)
1212 {
1213   g_return_if_fail (self->func != NULL);
1214
1215   gst_parallelized_task_runner_join (self);
1216
1217   self->func = NULL;
1218   self->task_data = NULL;
1219 }
1220
1221 static void
1222 gst_parallelized_task_runner_run (GstParallelizedTaskRunner * self,
1223     GstParallelizedTaskFunc func, gpointer * task_data)
1224 {
1225   guint n_threads = self->n_threads;
1226
1227   self->func = func;
1228   self->task_data = task_data;
1229
1230   if (n_threads > 1 || self->async_tasks) {
1231     guint i = 0;
1232     g_mutex_lock (&self->lock);
1233     self->n_todo = self->n_threads - 1;
1234     if (!self->async_tasks) {
1235       /* if not async, perform one of the functions in the current thread */
1236       self->n_todo--;
1237       i = 1;
1238     }
1239     for (; i < n_threads; i++) {
1240       gpointer task =
1241           gst_task_pool_push (self->pool, gst_parallelized_task_thread_func,
1242           self, NULL);
1243
1244       /* The return value of push() is nullable but NULL is only returned
1245        * with the shared task pool when gst_task_pool_prepare() has not been
1246        * called and would thus be a programming error that we should hard-fail
1247        * on.
1248        */
1249       g_assert (task != NULL);
1250       gst_queue_array_push_tail (self->tasks, task);
1251     }
1252     g_mutex_unlock (&self->lock);
1253   }
1254
1255   if (!self->async_tasks) {
1256     self->func (self->task_data[self->n_threads - 1]);
1257
1258     gst_parallelized_task_runner_finish (self);
1259   }
1260 }
1261
1262 static gboolean
1263 _negotiated_caps (GstAggregator * agg, GstCaps * caps)
1264 {
1265   GstCompositor *compositor = GST_COMPOSITOR (agg);
1266   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
1267   GstVideoInfo v_info;
1268   guint n_threads;
1269
1270   GST_DEBUG_OBJECT (agg, "Negotiated caps %" GST_PTR_FORMAT, caps);
1271
1272   if (!gst_video_info_from_caps (&v_info, caps))
1273     return FALSE;
1274
1275   if (!set_functions (compositor, &v_info)) {
1276     GST_ERROR_OBJECT (agg, "Failed to setup vfuncs");
1277     return FALSE;
1278   }
1279
1280   if (compositor->max_threads == 0)
1281     n_threads = g_get_num_processors ();
1282   else
1283     n_threads = compositor->max_threads;
1284
1285   /* Magic number of 200 lines */
1286   if (GST_VIDEO_INFO_HEIGHT (&v_info) / n_threads < 200)
1287     n_threads = (GST_VIDEO_INFO_HEIGHT (&v_info) + 199) / 200;
1288   if (n_threads < 1)
1289     n_threads = 1;
1290
1291   /* XXX: implement better thread count change */
1292   if (compositor->blend_runner
1293       && compositor->blend_runner->n_threads != n_threads) {
1294     gst_parallelized_task_runner_free (compositor->blend_runner);
1295     compositor->blend_runner = NULL;
1296   }
1297   if (!compositor->blend_runner) {
1298     GstTaskPool *pool = gst_video_aggregator_get_execution_task_pool (vagg);
1299     compositor->blend_runner =
1300         gst_parallelized_task_runner_new (n_threads, pool, FALSE);
1301     gst_clear_object (&pool);
1302   }
1303
1304   return GST_AGGREGATOR_CLASS (parent_class)->negotiated_src_caps (agg, caps);
1305 }
1306
1307 static gboolean
1308 _should_draw_background (GstVideoAggregator * vagg)
1309 {
1310   GstVideoRectangle bg_rect;
1311   gboolean draw = TRUE;
1312   GList *l;
1313
1314   bg_rect.x = bg_rect.y = 0;
1315
1316   GST_OBJECT_LOCK (vagg);
1317   bg_rect.w = GST_VIDEO_INFO_WIDTH (&vagg->info);
1318   bg_rect.h = GST_VIDEO_INFO_HEIGHT (&vagg->info);
1319   /* Check if the background is completely obscured by a pad
1320    * TODO: Also skip if it's obscured by a combination of pads */
1321   for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
1322     if (gst_aggregator_pad_is_inactive (GST_AGGREGATOR_PAD (l->data))
1323         ||
1324         gst_video_aggregator_pad_get_prepared_frame (GST_VIDEO_AGGREGATOR_PAD
1325             (l->data)) == NULL)
1326       continue;
1327
1328     if (_pad_obscures_rectangle (vagg, l->data, bg_rect)) {
1329       draw = FALSE;
1330       break;
1331     }
1332   }
1333   GST_OBJECT_UNLOCK (vagg);
1334   return draw;
1335 }
1336
1337 static gboolean
1338 frames_can_copy (const GstVideoFrame * frame1, const GstVideoFrame * frame2)
1339 {
1340   if (GST_VIDEO_FRAME_FORMAT (frame1) != GST_VIDEO_FRAME_FORMAT (frame2))
1341     return FALSE;
1342   if (GST_VIDEO_FRAME_HEIGHT (frame1) != GST_VIDEO_FRAME_HEIGHT (frame2))
1343     return FALSE;
1344   if (GST_VIDEO_FRAME_WIDTH (frame1) != GST_VIDEO_FRAME_WIDTH (frame2))
1345     return FALSE;
1346   return TRUE;
1347 }
1348
1349 struct CompositePadInfo
1350 {
1351   GstVideoFrame *prepared_frame;
1352   GstCompositorPad *pad;
1353   GstCompositorBlendMode blend_mode;
1354 };
1355
1356 struct CompositeTask
1357 {
1358   GstCompositor *compositor;
1359   GstVideoFrame *out_frame;
1360   guint dst_line_start;
1361   guint dst_line_end;
1362   gboolean draw_background;
1363   guint n_pads;
1364   struct CompositePadInfo *pads_info;
1365 };
1366
1367 static void
1368 _draw_background (GstCompositor * comp, GstVideoFrame * outframe,
1369     guint y_start, guint y_end, BlendFunction * composite)
1370 {
1371   *composite = comp->blend;
1372
1373   switch (comp->background) {
1374     case COMPOSITOR_BACKGROUND_CHECKER:
1375       comp->fill_checker (outframe, y_start, y_end);
1376       break;
1377     case COMPOSITOR_BACKGROUND_BLACK:
1378       comp->fill_color (outframe, y_start, y_end,
1379           comp->black_color[GST_VIDEO_COMP_Y],
1380           comp->black_color[GST_VIDEO_COMP_U],
1381           comp->black_color[GST_VIDEO_COMP_V]);
1382       break;
1383     case COMPOSITOR_BACKGROUND_WHITE:
1384       comp->fill_color (outframe, y_start, y_end,
1385           comp->white_color[GST_VIDEO_COMP_Y],
1386           comp->white_color[GST_VIDEO_COMP_U],
1387           comp->white_color[GST_VIDEO_COMP_V]);
1388       break;
1389     case COMPOSITOR_BACKGROUND_TRANSPARENT:
1390     {
1391       guint i, plane, num_planes, height;
1392
1393       num_planes = GST_VIDEO_FRAME_N_PLANES (outframe);
1394       for (plane = 0; plane < num_planes; ++plane) {
1395         const GstVideoFormatInfo *info;
1396         gint comp[GST_VIDEO_MAX_COMPONENTS];
1397         guint8 *pdata;
1398         gsize rowsize, plane_stride;
1399         gint yoffset;
1400
1401         info = outframe->info.finfo;
1402         pdata = GST_VIDEO_FRAME_PLANE_DATA (outframe, plane);
1403         plane_stride = GST_VIDEO_FRAME_PLANE_STRIDE (outframe, plane);
1404
1405         gst_video_format_info_component (info, plane, comp);
1406         rowsize = GST_VIDEO_FRAME_COMP_WIDTH (outframe, comp[0])
1407             * GST_VIDEO_FRAME_COMP_PSTRIDE (outframe, comp[0]);
1408         height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, comp[0],
1409             (y_end - y_start));
1410
1411         yoffset = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, comp[0], y_start);
1412
1413         pdata += yoffset * plane_stride;
1414         for (i = 0; i < height; ++i) {
1415           memset (pdata, 0, rowsize);
1416           pdata += plane_stride;
1417         }
1418       }
1419       /* use overlay to keep background transparent */
1420       *composite = comp->overlay;
1421       break;
1422     }
1423   }
1424 }
1425
1426 static void
1427 blend_pads (struct CompositeTask *comp)
1428 {
1429   BlendFunction composite;
1430   guint i;
1431
1432   composite = comp->compositor->blend;
1433
1434   if (comp->draw_background) {
1435     _draw_background (comp->compositor, comp->out_frame, comp->dst_line_start,
1436         comp->dst_line_end, &composite);
1437   }
1438
1439   for (i = 0; i < comp->n_pads; i++) {
1440     composite (comp->pads_info[i].prepared_frame,
1441         comp->pads_info[i].pad->xpos + comp->pads_info[i].pad->x_offset,
1442         comp->pads_info[i].pad->ypos + comp->pads_info[i].pad->y_offset,
1443         comp->pads_info[i].pad->alpha, comp->out_frame, comp->dst_line_start,
1444         comp->dst_line_end, comp->pads_info[i].blend_mode);
1445   }
1446 }
1447
1448 static GstFlowReturn
1449 gst_compositor_aggregate_frames (GstVideoAggregator * vagg, GstBuffer * outbuf)
1450 {
1451   GstCompositor *compositor = GST_COMPOSITOR (vagg);
1452   GList *l;
1453   GstVideoFrame out_frame, *outframe;
1454   gboolean draw_background;
1455   guint drawn_a_pad = FALSE;
1456   struct CompositePadInfo *pads_info;
1457   guint i, n_pads = 0;
1458
1459   if (!gst_video_frame_map (&out_frame, &vagg->info, outbuf, GST_MAP_WRITE)) {
1460     GST_WARNING_OBJECT (vagg, "Could not map output buffer");
1461     return GST_FLOW_ERROR;
1462   }
1463
1464   outframe = &out_frame;
1465
1466   /* If one of the frames to be composited completely obscures the background,
1467    * don't bother drawing the background at all. We can also always use the
1468    * 'blend' BlendFunction in that case because it only changes if we have to
1469    * overlay on top of a transparent background. */
1470   draw_background = _should_draw_background (vagg);
1471
1472   GST_OBJECT_LOCK (vagg);
1473   for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
1474     GstVideoAggregatorPad *pad = l->data;
1475     GstVideoFrame *prepared_frame =
1476         gst_video_aggregator_pad_get_prepared_frame (pad);
1477
1478     if (prepared_frame)
1479       n_pads++;
1480   }
1481
1482   /* If no prepared frame, we should draw background unconditionally in order
1483    * to clear output buffer */
1484   if (n_pads == 0)
1485     draw_background = TRUE;
1486
1487   pads_info = g_newa (struct CompositePadInfo, n_pads);
1488   n_pads = 0;
1489
1490   for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
1491     GstVideoAggregatorPad *pad = l->data;
1492     GstCompositorPad *compo_pad = GST_COMPOSITOR_PAD (pad);
1493     GstVideoFrame *prepared_frame =
1494         gst_video_aggregator_pad_get_prepared_frame (pad);
1495     GstCompositorBlendMode blend_mode = COMPOSITOR_BLEND_MODE_OVER;
1496
1497     switch (compo_pad->op) {
1498       case COMPOSITOR_OPERATOR_SOURCE:
1499         blend_mode = COMPOSITOR_BLEND_MODE_SOURCE;
1500         break;
1501       case COMPOSITOR_OPERATOR_OVER:
1502         blend_mode = COMPOSITOR_BLEND_MODE_OVER;
1503         break;
1504       case COMPOSITOR_OPERATOR_ADD:
1505         blend_mode = COMPOSITOR_BLEND_MODE_ADD;
1506         break;
1507       default:
1508         g_assert_not_reached ();
1509         break;
1510     }
1511
1512     if (prepared_frame != NULL) {
1513       /* If this is the first pad we're drawing, and we didn't draw the
1514        * background, and @prepared_frame has the same format, height, and width
1515        * as @outframe, then we can just copy it as-is. Subsequent pads (if any)
1516        * will be composited on top of it. */
1517       if (!drawn_a_pad && !draw_background &&
1518           frames_can_copy (prepared_frame, outframe)) {
1519         gst_video_frame_copy (outframe, prepared_frame);
1520       } else {
1521         pads_info[n_pads].pad = compo_pad;
1522         pads_info[n_pads].prepared_frame = prepared_frame;
1523         pads_info[n_pads].blend_mode = blend_mode;
1524         n_pads++;
1525       }
1526       drawn_a_pad = TRUE;
1527     }
1528   }
1529
1530   {
1531     guint n_threads, lines_per_thread;
1532     guint out_height;
1533     struct CompositeTask *tasks;
1534     struct CompositeTask **tasks_p;
1535
1536     n_threads = compositor->blend_runner->n_threads;
1537
1538     tasks = g_newa (struct CompositeTask, n_threads);
1539     tasks_p = g_newa (struct CompositeTask *, n_threads);
1540
1541     out_height = GST_VIDEO_FRAME_HEIGHT (outframe);
1542     lines_per_thread = (out_height + n_threads - 1) / n_threads;
1543
1544     for (i = 0; i < n_threads; i++) {
1545       tasks[i].compositor = compositor;
1546       tasks[i].n_pads = n_pads;
1547       tasks[i].pads_info = pads_info;
1548       tasks[i].out_frame = outframe;
1549       tasks[i].draw_background = draw_background;
1550       /* This is a dumb split of the work by number of output lines.
1551        * If there is a section of the output that reads from a lot of source
1552        * pads, then that thread will consume more time. Maybe tracking and
1553        * splitting on the source fill rate would produce better results. */
1554       tasks[i].dst_line_start = i * lines_per_thread;
1555       tasks[i].dst_line_end = MIN ((i + 1) * lines_per_thread, out_height);
1556
1557       tasks_p[i] = &tasks[i];
1558     }
1559
1560     gst_parallelized_task_runner_run (compositor->blend_runner,
1561         (GstParallelizedTaskFunc) blend_pads, (gpointer *) tasks_p);
1562   }
1563
1564   GST_OBJECT_UNLOCK (vagg);
1565
1566   gst_video_frame_unmap (outframe);
1567
1568   return GST_FLOW_OK;
1569 }
1570
1571 static GstPad *
1572 gst_compositor_request_new_pad (GstElement * element, GstPadTemplate * templ,
1573     const gchar * req_name, const GstCaps * caps)
1574 {
1575   GstPad *newpad;
1576
1577   newpad = (GstPad *)
1578       GST_ELEMENT_CLASS (parent_class)->request_new_pad (element,
1579       templ, req_name, caps);
1580
1581   if (newpad == NULL)
1582     goto could_not_create;
1583
1584   gst_child_proxy_child_added (GST_CHILD_PROXY (element), G_OBJECT (newpad),
1585       GST_OBJECT_NAME (newpad));
1586
1587   return newpad;
1588
1589 could_not_create:
1590   {
1591     GST_DEBUG_OBJECT (element, "could not create/add pad");
1592     return NULL;
1593   }
1594 }
1595
1596 static void
1597 gst_compositor_release_pad (GstElement * element, GstPad * pad)
1598 {
1599   GstCompositor *compositor;
1600
1601   compositor = GST_COMPOSITOR (element);
1602
1603   GST_DEBUG_OBJECT (compositor, "release pad %s:%s", GST_DEBUG_PAD_NAME (pad));
1604
1605   gst_child_proxy_child_removed (GST_CHILD_PROXY (compositor), G_OBJECT (pad),
1606       GST_OBJECT_NAME (pad));
1607
1608   GST_ELEMENT_CLASS (parent_class)->release_pad (element, pad);
1609 }
1610
1611 static gboolean
1612 src_pad_mouse_event (GstElement * element, GstPad * pad, gpointer user_data)
1613 {
1614   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR_CAST (element);
1615   GstCompositor *comp = GST_COMPOSITOR (element);
1616   GstCompositorPad *cpad = GST_COMPOSITOR_PAD (pad);
1617   GstStructure *st =
1618       gst_structure_copy (gst_event_get_structure (GST_EVENT_CAST (user_data)));
1619   gdouble event_x, event_y;
1620   gint offset_x, offset_y;
1621   GstVideoRectangle rect;
1622
1623   gst_structure_get (st, "pointer_x", G_TYPE_DOUBLE, &event_x,
1624       "pointer_y", G_TYPE_DOUBLE, &event_y, NULL);
1625
1626   /* Find output rectangle of this pad */
1627   _mixer_pad_get_output_size (comp, cpad,
1628       GST_VIDEO_INFO_PAR_N (&vagg->info),
1629       GST_VIDEO_INFO_PAR_D (&vagg->info),
1630       &(rect.w), &(rect.h), &offset_x, &offset_y);
1631   rect.x = cpad->xpos + offset_x;
1632   rect.y = cpad->ypos + offset_y;
1633
1634   /* Translate coordinates and send event if it lies in this rectangle */
1635   if (is_point_contained (rect, event_x, event_y)) {
1636     GstVideoAggregatorPad *vpad = GST_VIDEO_AGGREGATOR_PAD_CAST (cpad);
1637     gdouble w, h, x, y;
1638
1639     w = (gdouble) GST_VIDEO_INFO_WIDTH (&vpad->info);
1640     h = (gdouble) GST_VIDEO_INFO_HEIGHT (&vpad->info);
1641     x = (event_x - (gdouble) rect.x) * (w / (gdouble) rect.w);
1642     y = (event_y - (gdouble) rect.y) * (h / (gdouble) rect.h);
1643
1644     gst_structure_set (st, "pointer_x", G_TYPE_DOUBLE, x,
1645         "pointer_y", G_TYPE_DOUBLE, y, NULL);
1646     gst_pad_push_event (pad, gst_event_new_navigation (st));
1647   } else {
1648     gst_structure_free (st);
1649   }
1650
1651   return TRUE;
1652 }
1653
1654 static gboolean
1655 _src_event (GstAggregator * agg, GstEvent * event)
1656 {
1657   GstNavigationEventType event_type;
1658
1659   switch (GST_EVENT_TYPE (event)) {
1660     case GST_EVENT_NAVIGATION:
1661     {
1662       event_type = gst_navigation_event_get_type (event);
1663       switch (event_type) {
1664         case GST_NAVIGATION_EVENT_MOUSE_BUTTON_PRESS:
1665         case GST_NAVIGATION_EVENT_MOUSE_BUTTON_RELEASE:
1666         case GST_NAVIGATION_EVENT_MOUSE_MOVE:
1667         case GST_NAVIGATION_EVENT_MOUSE_SCROLL:
1668           gst_element_foreach_sink_pad (GST_ELEMENT_CAST (agg),
1669               src_pad_mouse_event, event);
1670           gst_event_unref (event);
1671           return TRUE;
1672
1673         default:
1674           break;
1675       }
1676     }
1677     default:
1678       break;
1679   }
1680
1681   return GST_AGGREGATOR_CLASS (parent_class)->src_event (agg, event);
1682 }
1683
1684 static gboolean
1685 _sink_query (GstAggregator * agg, GstAggregatorPad * bpad, GstQuery * query)
1686 {
1687   switch (GST_QUERY_TYPE (query)) {
1688     case GST_QUERY_ALLOCATION:{
1689       GstCaps *caps;
1690       GstVideoInfo info;
1691       GstBufferPool *pool;
1692       guint size;
1693       GstStructure *structure;
1694
1695       gst_query_parse_allocation (query, &caps, NULL);
1696
1697       if (caps == NULL)
1698         return FALSE;
1699
1700       if (!gst_video_info_from_caps (&info, caps))
1701         return FALSE;
1702
1703       size = GST_VIDEO_INFO_SIZE (&info);
1704
1705       pool = gst_video_buffer_pool_new ();
1706
1707       structure = gst_buffer_pool_get_config (pool);
1708       gst_buffer_pool_config_set_params (structure, caps, size, 0, 0);
1709
1710       if (!gst_buffer_pool_set_config (pool, structure)) {
1711         gst_object_unref (pool);
1712         return FALSE;
1713       }
1714
1715       gst_query_add_allocation_pool (query, pool, size, 0, 0);
1716       gst_object_unref (pool);
1717       gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
1718
1719       return TRUE;
1720     }
1721     default:
1722       return GST_AGGREGATOR_CLASS (parent_class)->sink_query (agg, bpad, query);
1723   }
1724 }
1725
1726 static void
1727 gst_compositor_finalize (GObject * object)
1728 {
1729   GstCompositor *compositor = GST_COMPOSITOR (object);
1730
1731   if (compositor->blend_runner)
1732     gst_parallelized_task_runner_free (compositor->blend_runner);
1733   compositor->blend_runner = NULL;
1734
1735   G_OBJECT_CLASS (parent_class)->finalize (object);
1736 }
1737
1738 /* GObject boilerplate */
1739 static void
1740 gst_compositor_class_init (GstCompositorClass * klass)
1741 {
1742   GObjectClass *gobject_class = (GObjectClass *) klass;
1743   GstElementClass *gstelement_class = (GstElementClass *) klass;
1744   GstVideoAggregatorClass *videoaggregator_class =
1745       (GstVideoAggregatorClass *) klass;
1746   GstAggregatorClass *agg_class = (GstAggregatorClass *) klass;
1747
1748   gobject_class->get_property = gst_compositor_get_property;
1749   gobject_class->set_property = gst_compositor_set_property;
1750   gobject_class->finalize = gst_compositor_finalize;
1751
1752   gstelement_class->request_new_pad =
1753       GST_DEBUG_FUNCPTR (gst_compositor_request_new_pad);
1754   gstelement_class->release_pad =
1755       GST_DEBUG_FUNCPTR (gst_compositor_release_pad);
1756   agg_class->sink_query = _sink_query;
1757   agg_class->src_event = _src_event;
1758   agg_class->fixate_src_caps = _fixate_caps;
1759   agg_class->negotiated_src_caps = _negotiated_caps;
1760   videoaggregator_class->aggregate_frames = gst_compositor_aggregate_frames;
1761
1762   g_object_class_install_property (gobject_class, PROP_BACKGROUND,
1763       g_param_spec_enum ("background", "Background", "Background type",
1764           GST_TYPE_COMPOSITOR_BACKGROUND,
1765           DEFAULT_BACKGROUND, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1766
1767   /**
1768    * compositor:zero-size-is-unscaled:
1769    *
1770    * Whether a pad with height or width 0 should be left unscaled
1771    * in that dimension, or simply not composited in. Setting it to
1772    * %FALSE might be useful when animating those properties.
1773    *
1774    * Since: 1.20
1775    */
1776   g_object_class_install_property (gobject_class, PROP_ZERO_SIZE_IS_UNSCALED,
1777       g_param_spec_boolean ("zero-size-is-unscaled", "Zero size is unscaled",
1778           "If TRUE, then input video is unscaled in that dimension "
1779           "if width or height is 0 (for backwards compatibility)",
1780           DEFAULT_ZERO_SIZE_IS_UNSCALED,
1781           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1782
1783   /**
1784    * compositor:max-threads:
1785    *
1786    * Maximum number of blending/rendering worker threads to spawn (0 = auto)
1787    *
1788    * Since: 1.20
1789    */
1790   g_object_class_install_property (gobject_class, PROP_MAX_THREADS,
1791       g_param_spec_uint ("max-threads", "Max Threads",
1792           "Maximum number of blending/rendering worker threads to spawn "
1793           "(0 = auto)", 0, G_MAXINT, DEFAULT_MAX_THREADS,
1794           GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE |
1795           G_PARAM_STATIC_STRINGS));
1796
1797   gst_element_class_add_static_pad_template_with_gtype (gstelement_class,
1798       &src_factory, GST_TYPE_AGGREGATOR_PAD);
1799   gst_element_class_add_static_pad_template_with_gtype (gstelement_class,
1800       &sink_factory, GST_TYPE_COMPOSITOR_PAD);
1801
1802   gst_element_class_set_static_metadata (gstelement_class, "Compositor",
1803       "Filter/Editor/Video/Compositor",
1804       "Composite multiple video streams", "Wim Taymans <wim@fluendo.com>, "
1805       "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
1806
1807   /**
1808    * compositor:ignore-inactive-pads:
1809    *
1810    * Don't wait for inactive pads when live. An inactive pad
1811    * is a pad that hasn't yet received a buffer, but that has
1812    * been waited on at least once.
1813    *
1814    * The purpose of this property is to avoid aggregating on
1815    * timeout when new pads are requested in advance of receiving
1816    * data flow, for example the user may decide to connect it later,
1817    * but wants to configure it already.
1818    *
1819    * Since: 1.20
1820    */
1821   g_object_class_install_property (gobject_class,
1822       PROP_IGNORE_INACTIVE_PADS, g_param_spec_boolean ("ignore-inactive-pads",
1823           "Ignore inactive pads",
1824           "Avoid timing out waiting for inactive pads", FALSE,
1825           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1826
1827   gst_type_mark_as_plugin_api (GST_TYPE_COMPOSITOR_PAD, 0);
1828   gst_type_mark_as_plugin_api (GST_TYPE_COMPOSITOR_OPERATOR, 0);
1829   gst_type_mark_as_plugin_api (GST_TYPE_COMPOSITOR_BACKGROUND, 0);
1830 }
1831
1832 static void
1833 gst_compositor_init (GstCompositor * self)
1834 {
1835   /* initialize variables */
1836   self->background = DEFAULT_BACKGROUND;
1837   self->zero_size_is_unscaled = DEFAULT_ZERO_SIZE_IS_UNSCALED;
1838   self->max_threads = DEFAULT_MAX_THREADS;
1839 }
1840
1841 /* GstChildProxy implementation */
1842 static GObject *
1843 gst_compositor_child_proxy_get_child_by_index (GstChildProxy * child_proxy,
1844     guint index)
1845 {
1846   GstCompositor *compositor = GST_COMPOSITOR (child_proxy);
1847   GObject *obj = NULL;
1848
1849   GST_OBJECT_LOCK (compositor);
1850   obj = g_list_nth_data (GST_ELEMENT_CAST (compositor)->sinkpads, index);
1851   if (obj)
1852     gst_object_ref (obj);
1853   GST_OBJECT_UNLOCK (compositor);
1854
1855   return obj;
1856 }
1857
1858 static guint
1859 gst_compositor_child_proxy_get_children_count (GstChildProxy * child_proxy)
1860 {
1861   guint count = 0;
1862   GstCompositor *compositor = GST_COMPOSITOR (child_proxy);
1863
1864   GST_OBJECT_LOCK (compositor);
1865   count = GST_ELEMENT_CAST (compositor)->numsinkpads;
1866   GST_OBJECT_UNLOCK (compositor);
1867   GST_INFO_OBJECT (compositor, "Children Count: %d", count);
1868
1869   return count;
1870 }
1871
1872 static void
1873 gst_compositor_child_proxy_init (gpointer g_iface, gpointer iface_data)
1874 {
1875   GstChildProxyInterface *iface = g_iface;
1876
1877   iface->get_child_by_index = gst_compositor_child_proxy_get_child_by_index;
1878   iface->get_children_count = gst_compositor_child_proxy_get_children_count;
1879 }
1880
1881 /* Element registration */
1882 static gboolean
1883 plugin_init (GstPlugin * plugin)
1884 {
1885   GST_DEBUG_CATEGORY_INIT (gst_compositor_debug, "compositor", 0, "compositor");
1886
1887   gst_compositor_init_blend ();
1888
1889   return GST_ELEMENT_REGISTER (compositor, plugin);
1890 }
1891
1892 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1893     GST_VERSION_MINOR,
1894     compositor,
1895     "Compositor", plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME,
1896     GST_PACKAGE_ORIGIN)