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