plugins: uddate gst_type_mark_as_plugin_api() calls
[platform/upstream/gstreamer.git] / 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 (FORMATS))
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 DEFAULT_PAD_XPOS   0
169 #define DEFAULT_PAD_YPOS   0
170 #define DEFAULT_PAD_WIDTH  0
171 #define DEFAULT_PAD_HEIGHT 0
172 #define DEFAULT_PAD_ALPHA  1.0
173 #define DEFAULT_PAD_OPERATOR COMPOSITOR_OPERATOR_OVER
174 enum
175 {
176   PROP_PAD_0,
177   PROP_PAD_XPOS,
178   PROP_PAD_YPOS,
179   PROP_PAD_WIDTH,
180   PROP_PAD_HEIGHT,
181   PROP_PAD_ALPHA,
182   PROP_PAD_OPERATOR,
183 };
184
185 G_DEFINE_TYPE (GstCompositorPad, gst_compositor_pad,
186     GST_TYPE_VIDEO_AGGREGATOR_CONVERT_PAD);
187
188 static void
189 gst_compositor_pad_get_property (GObject * object, guint prop_id,
190     GValue * value, GParamSpec * pspec)
191 {
192   GstCompositorPad *pad = GST_COMPOSITOR_PAD (object);
193
194   switch (prop_id) {
195     case PROP_PAD_XPOS:
196       g_value_set_int (value, pad->xpos);
197       break;
198     case PROP_PAD_YPOS:
199       g_value_set_int (value, pad->ypos);
200       break;
201     case PROP_PAD_WIDTH:
202       g_value_set_int (value, pad->width);
203       break;
204     case PROP_PAD_HEIGHT:
205       g_value_set_int (value, pad->height);
206       break;
207     case PROP_PAD_ALPHA:
208       g_value_set_double (value, pad->alpha);
209       break;
210     case PROP_PAD_OPERATOR:
211       g_value_set_enum (value, pad->op);
212       break;
213     default:
214       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
215       break;
216   }
217 }
218
219 static void
220 gst_compositor_pad_set_property (GObject * object, guint prop_id,
221     const GValue * value, GParamSpec * pspec)
222 {
223   GstCompositorPad *pad = GST_COMPOSITOR_PAD (object);
224
225   switch (prop_id) {
226     case PROP_PAD_XPOS:
227       pad->xpos = g_value_get_int (value);
228       break;
229     case PROP_PAD_YPOS:
230       pad->ypos = g_value_get_int (value);
231       break;
232     case PROP_PAD_WIDTH:
233       pad->width = g_value_get_int (value);
234       gst_video_aggregator_convert_pad_update_conversion_info
235           (GST_VIDEO_AGGREGATOR_CONVERT_PAD (pad));
236       break;
237     case PROP_PAD_HEIGHT:
238       pad->height = g_value_get_int (value);
239       gst_video_aggregator_convert_pad_update_conversion_info
240           (GST_VIDEO_AGGREGATOR_CONVERT_PAD (pad));
241       break;
242     case PROP_PAD_ALPHA:
243       pad->alpha = g_value_get_double (value);
244       break;
245     case PROP_PAD_OPERATOR:
246       pad->op = g_value_get_enum (value);
247       gst_video_aggregator_pad_set_needs_alpha (GST_VIDEO_AGGREGATOR_PAD (pad),
248           pad->op == COMPOSITOR_OPERATOR_ADD);
249       break;
250     default:
251       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
252       break;
253   }
254 }
255
256 static void
257 _mixer_pad_get_output_size (GstCompositorPad * comp_pad, gint out_par_n,
258     gint out_par_d, gint * width, gint * height)
259 {
260   GstVideoAggregatorPad *vagg_pad = GST_VIDEO_AGGREGATOR_PAD (comp_pad);
261   gint pad_width, pad_height;
262   guint dar_n, dar_d;
263
264   /* FIXME: Anything better we can do here? */
265   if (!vagg_pad->info.finfo
266       || vagg_pad->info.finfo->format == GST_VIDEO_FORMAT_UNKNOWN) {
267     GST_DEBUG_OBJECT (comp_pad, "Have no caps yet");
268     *width = 0;
269     *height = 0;
270     return;
271   }
272
273   pad_width =
274       comp_pad->width <=
275       0 ? GST_VIDEO_INFO_WIDTH (&vagg_pad->info) : comp_pad->width;
276   pad_height =
277       comp_pad->height <=
278       0 ? GST_VIDEO_INFO_HEIGHT (&vagg_pad->info) : comp_pad->height;
279
280   if (!gst_video_calculate_display_ratio (&dar_n, &dar_d, pad_width, pad_height,
281           GST_VIDEO_INFO_PAR_N (&vagg_pad->info),
282           GST_VIDEO_INFO_PAR_D (&vagg_pad->info), out_par_n, out_par_d)) {
283     GST_WARNING_OBJECT (comp_pad, "Cannot calculate display aspect ratio");
284     *width = *height = 0;
285     return;
286   }
287   GST_LOG_OBJECT (comp_pad, "scaling %ux%u by %u/%u (%u/%u / %u/%u)", pad_width,
288       pad_height, dar_n, dar_d, GST_VIDEO_INFO_PAR_N (&vagg_pad->info),
289       GST_VIDEO_INFO_PAR_D (&vagg_pad->info), out_par_n, out_par_d);
290
291   /* Pick either height or width, whichever is an integer multiple of the
292    * display aspect ratio. However, prefer preserving the height to account
293    * for interlaced video. */
294   if (pad_height % dar_n == 0) {
295     pad_width = gst_util_uint64_scale_int (pad_height, dar_n, dar_d);
296   } else if (pad_width % dar_d == 0) {
297     pad_height = gst_util_uint64_scale_int (pad_width, dar_d, dar_n);
298   } else {
299     pad_width = gst_util_uint64_scale_int (pad_height, dar_n, dar_d);
300   }
301
302   *width = pad_width;
303   *height = pad_height;
304 }
305
306 /* Test whether rectangle2 contains rectangle 1 (geometrically) */
307 static gboolean
308 is_rectangle_contained (const GstVideoRectangle rect1,
309     const GstVideoRectangle rect2)
310 {
311   if ((rect2.x <= rect1.x) && (rect2.y <= rect1.y) &&
312       ((rect2.x + rect2.w) >= (rect1.x + rect1.w)) &&
313       ((rect2.y + rect2.h) >= (rect1.y + rect1.h)))
314     return TRUE;
315   return FALSE;
316 }
317
318 static GstVideoRectangle
319 clamp_rectangle (gint x, gint y, gint w, gint h, gint outer_width,
320     gint outer_height)
321 {
322   gint x2 = x + w;
323   gint y2 = y + h;
324   GstVideoRectangle clamped;
325
326   /* Clamp the x/y coordinates of this frame to the output boundaries to cover
327    * the case where (say, with negative xpos/ypos or w/h greater than the output
328    * size) the non-obscured portion of the frame could be outside the bounds of
329    * the video itself and hence not visible at all */
330   clamped.x = CLAMP (x, 0, outer_width);
331   clamped.y = CLAMP (y, 0, outer_height);
332   clamped.w = CLAMP (x2, 0, outer_width) - clamped.x;
333   clamped.h = CLAMP (y2, 0, outer_height) - clamped.y;
334
335   return clamped;
336 }
337
338 /* Call this with the lock taken */
339 static gboolean
340 _pad_obscures_rectangle (GstVideoAggregator * vagg, GstVideoAggregatorPad * pad,
341     const GstVideoRectangle rect, gboolean rect_transparent)
342 {
343   GstVideoRectangle pad_rect;
344   GstCompositorPad *cpad = GST_COMPOSITOR_PAD (pad);
345
346   /* No buffer to obscure the rectangle with */
347   if (!gst_video_aggregator_pad_has_current_buffer (pad))
348     return FALSE;
349
350   /* Can't obscure if it's transparent and if the format has an alpha
351    * component we'd have to inspect every pixel to know if the frame is
352    * opaque, so assume it doesn't obscure unless it uses the SOURCE operator.
353    * As a bonus, if the rectangle is fully transparent, we can also obscure it
354    * if we have alpha components on the pad */
355   if (cpad->op != COMPOSITOR_OPERATOR_SOURCE && !rect_transparent &&
356       (cpad->alpha != 1.0 || GST_VIDEO_INFO_HAS_ALPHA (&pad->info)))
357     return FALSE;
358
359   pad_rect.x = cpad->xpos;
360   pad_rect.y = cpad->ypos;
361   /* Handle pixel and display aspect ratios to find the actual size */
362   _mixer_pad_get_output_size (cpad, GST_VIDEO_INFO_PAR_N (&vagg->info),
363       GST_VIDEO_INFO_PAR_D (&vagg->info), &(pad_rect.w), &(pad_rect.h));
364
365   if (!is_rectangle_contained (rect, pad_rect))
366     return FALSE;
367
368   GST_DEBUG_OBJECT (pad, "Pad %s %ix%i@(%i,%i) obscures rect %ix%i@(%i,%i)",
369       GST_PAD_NAME (pad), pad_rect.w, pad_rect.h, pad_rect.x, pad_rect.y,
370       rect.w, rect.h, rect.x, rect.y);
371
372   return TRUE;
373 }
374
375 static gboolean
376 gst_compositor_pad_prepare_frame (GstVideoAggregatorPad * pad,
377     GstVideoAggregator * vagg, GstBuffer * buffer,
378     GstVideoFrame * prepared_frame)
379 {
380   GstCompositorPad *cpad = GST_COMPOSITOR_PAD (pad);
381   gint width, height;
382   gboolean frame_obscured = FALSE;
383   GList *l;
384   /* The rectangle representing this frame, clamped to the video's boundaries.
385    * Due to the clamping, this is different from the frame width/height above. */
386   GstVideoRectangle frame_rect;
387
388   /* There's three types of width/height here:
389    * 1. GST_VIDEO_FRAME_WIDTH/HEIGHT:
390    *     The frame width/height (same as pad->info.height/width;
391    *     see gst_video_frame_map())
392    * 2. cpad->width/height:
393    *     The optional pad property for scaling the frame (if zero, the video is
394    *     left unscaled)
395    * 3. conversion_info.width/height:
396    *     Equal to cpad->width/height if it's set, otherwise it's the pad
397    *     width/height. See ->set_info()
398    * */
399
400   _mixer_pad_get_output_size (cpad, GST_VIDEO_INFO_PAR_N (&vagg->info),
401       GST_VIDEO_INFO_PAR_D (&vagg->info), &width, &height);
402
403   if (cpad->alpha == 0.0) {
404     GST_DEBUG_OBJECT (pad, "Pad has alpha 0.0, not converting frame");
405     goto done;
406   }
407
408   frame_rect = clamp_rectangle (cpad->xpos, cpad->ypos, width, height,
409       GST_VIDEO_INFO_WIDTH (&vagg->info), GST_VIDEO_INFO_HEIGHT (&vagg->info));
410
411   if (frame_rect.w == 0 || frame_rect.h == 0) {
412     GST_DEBUG_OBJECT (pad, "Resulting frame is zero-width or zero-height "
413         "(w: %i, h: %i), skipping", frame_rect.w, frame_rect.h);
414     goto done;
415   }
416
417   GST_OBJECT_LOCK (vagg);
418   /* Check if this frame is obscured by a higher-zorder frame
419    * TODO: Also skip a frame if it's obscured by a combination of
420    * higher-zorder frames */
421   l = g_list_find (GST_ELEMENT (vagg)->sinkpads, pad)->next;
422   for (; l; l = l->next) {
423     if (_pad_obscures_rectangle (vagg, l->data, frame_rect, FALSE)) {
424       frame_obscured = TRUE;
425       break;
426     }
427   }
428   GST_OBJECT_UNLOCK (vagg);
429
430   if (frame_obscured)
431     goto done;
432
433   return
434       GST_VIDEO_AGGREGATOR_PAD_CLASS
435       (gst_compositor_pad_parent_class)->prepare_frame (pad, vagg, buffer,
436       prepared_frame);
437
438 done:
439
440   return TRUE;
441 }
442
443 static void
444 gst_compositor_pad_create_conversion_info (GstVideoAggregatorConvertPad * pad,
445     GstVideoAggregator * vagg, GstVideoInfo * conversion_info)
446 {
447   GstCompositorPad *cpad = GST_COMPOSITOR_PAD (pad);
448   gint width, height;
449
450   GST_VIDEO_AGGREGATOR_CONVERT_PAD_CLASS
451       (gst_compositor_pad_parent_class)->create_conversion_info (pad, vagg,
452       conversion_info);
453   if (!conversion_info->finfo)
454     return;
455
456   _mixer_pad_get_output_size (cpad, GST_VIDEO_INFO_PAR_N (&vagg->info),
457       GST_VIDEO_INFO_PAR_D (&vagg->info), &width, &height);
458
459   /* The only thing that can change here is the width
460    * and height, otherwise set_info would've been called */
461   if (GST_VIDEO_INFO_WIDTH (conversion_info) != width ||
462       GST_VIDEO_INFO_HEIGHT (conversion_info) != height) {
463     GstVideoInfo tmp_info;
464
465     /* Initialize with the wanted video format and our original width and
466      * height as we don't want to rescale. Then copy over the wanted
467      * colorimetry, and chroma-site and our current pixel-aspect-ratio
468      * and other relevant fields.
469      */
470     gst_video_info_set_format (&tmp_info,
471         GST_VIDEO_INFO_FORMAT (conversion_info), width, height);
472     tmp_info.chroma_site = conversion_info->chroma_site;
473     tmp_info.colorimetry = conversion_info->colorimetry;
474     tmp_info.par_n = conversion_info->par_n;
475     tmp_info.par_d = conversion_info->par_d;
476     tmp_info.fps_n = conversion_info->fps_n;
477     tmp_info.fps_d = conversion_info->fps_d;
478     tmp_info.flags = conversion_info->flags;
479     tmp_info.interlace_mode = conversion_info->interlace_mode;
480
481     *conversion_info = tmp_info;
482   }
483 }
484
485 static void
486 gst_compositor_pad_class_init (GstCompositorPadClass * klass)
487 {
488   GObjectClass *gobject_class = (GObjectClass *) klass;
489   GstVideoAggregatorPadClass *vaggpadclass =
490       (GstVideoAggregatorPadClass *) klass;
491   GstVideoAggregatorConvertPadClass *vaggcpadclass =
492       (GstVideoAggregatorConvertPadClass *) klass;
493
494   gobject_class->set_property = gst_compositor_pad_set_property;
495   gobject_class->get_property = gst_compositor_pad_get_property;
496
497   g_object_class_install_property (gobject_class, PROP_PAD_XPOS,
498       g_param_spec_int ("xpos", "X Position", "X Position of the picture",
499           G_MININT, G_MAXINT, DEFAULT_PAD_XPOS,
500           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
501   g_object_class_install_property (gobject_class, PROP_PAD_YPOS,
502       g_param_spec_int ("ypos", "Y Position", "Y Position of the picture",
503           G_MININT, G_MAXINT, DEFAULT_PAD_YPOS,
504           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
505   g_object_class_install_property (gobject_class, PROP_PAD_WIDTH,
506       g_param_spec_int ("width", "Width", "Width of the picture",
507           G_MININT, G_MAXINT, DEFAULT_PAD_WIDTH,
508           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
509   g_object_class_install_property (gobject_class, PROP_PAD_HEIGHT,
510       g_param_spec_int ("height", "Height", "Height of the picture",
511           G_MININT, G_MAXINT, DEFAULT_PAD_HEIGHT,
512           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
513   g_object_class_install_property (gobject_class, PROP_PAD_ALPHA,
514       g_param_spec_double ("alpha", "Alpha", "Alpha of the picture", 0.0, 1.0,
515           DEFAULT_PAD_ALPHA,
516           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
517   g_object_class_install_property (gobject_class, PROP_PAD_OPERATOR,
518       g_param_spec_enum ("operator", "Operator",
519           "Blending operator to use for blending this pad over the previous ones",
520           GST_TYPE_COMPOSITOR_OPERATOR, DEFAULT_PAD_OPERATOR,
521           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
522
523   vaggpadclass->prepare_frame =
524       GST_DEBUG_FUNCPTR (gst_compositor_pad_prepare_frame);
525
526   vaggcpadclass->create_conversion_info =
527       GST_DEBUG_FUNCPTR (gst_compositor_pad_create_conversion_info);
528 }
529
530 static void
531 gst_compositor_pad_init (GstCompositorPad * compo_pad)
532 {
533   compo_pad->xpos = DEFAULT_PAD_XPOS;
534   compo_pad->ypos = DEFAULT_PAD_YPOS;
535   compo_pad->alpha = DEFAULT_PAD_ALPHA;
536   compo_pad->op = DEFAULT_PAD_OPERATOR;
537 }
538
539
540 /* GstCompositor */
541 #define DEFAULT_BACKGROUND COMPOSITOR_BACKGROUND_CHECKER
542 enum
543 {
544   PROP_0,
545   PROP_BACKGROUND,
546 };
547
548 static void
549 gst_compositor_get_property (GObject * object,
550     guint prop_id, GValue * value, GParamSpec * pspec)
551 {
552   GstCompositor *self = GST_COMPOSITOR (object);
553
554   switch (prop_id) {
555     case PROP_BACKGROUND:
556       g_value_set_enum (value, self->background);
557       break;
558     default:
559       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
560       break;
561   }
562 }
563
564 static void
565 gst_compositor_set_property (GObject * object,
566     guint prop_id, const GValue * value, GParamSpec * pspec)
567 {
568   GstCompositor *self = GST_COMPOSITOR (object);
569
570   switch (prop_id) {
571     case PROP_BACKGROUND:
572       self->background = g_value_get_enum (value);
573       break;
574     default:
575       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
576       break;
577   }
578 }
579
580 #define gst_compositor_parent_class parent_class
581 G_DEFINE_TYPE_WITH_CODE (GstCompositor, gst_compositor,
582     GST_TYPE_VIDEO_AGGREGATOR, G_IMPLEMENT_INTERFACE (GST_TYPE_CHILD_PROXY,
583         gst_compositor_child_proxy_init));
584
585 static gboolean
586 set_functions (GstCompositor * self, GstVideoInfo * info)
587 {
588   gboolean ret = FALSE;
589
590   self->blend = NULL;
591   self->overlay = NULL;
592   self->fill_checker = NULL;
593   self->fill_color = NULL;
594
595   switch (GST_VIDEO_INFO_FORMAT (info)) {
596     case GST_VIDEO_FORMAT_AYUV:
597       self->blend = gst_compositor_blend_ayuv;
598       self->overlay = gst_compositor_overlay_ayuv;
599       self->fill_checker = gst_compositor_fill_checker_ayuv;
600       self->fill_color = gst_compositor_fill_color_ayuv;
601       ret = TRUE;
602       break;
603     case GST_VIDEO_FORMAT_VUYA:
604       self->blend = gst_compositor_blend_vuya;
605       self->overlay = gst_compositor_overlay_vuya;
606       self->fill_checker = gst_compositor_fill_checker_vuya;
607       self->fill_color = gst_compositor_fill_color_vuya;
608       ret = TRUE;
609       break;
610     case GST_VIDEO_FORMAT_ARGB:
611       self->blend = gst_compositor_blend_argb;
612       self->overlay = gst_compositor_overlay_argb;
613       self->fill_checker = gst_compositor_fill_checker_argb;
614       self->fill_color = gst_compositor_fill_color_argb;
615       ret = TRUE;
616       break;
617     case GST_VIDEO_FORMAT_BGRA:
618       self->blend = gst_compositor_blend_bgra;
619       self->overlay = gst_compositor_overlay_bgra;
620       self->fill_checker = gst_compositor_fill_checker_bgra;
621       self->fill_color = gst_compositor_fill_color_bgra;
622       ret = TRUE;
623       break;
624     case GST_VIDEO_FORMAT_ABGR:
625       self->blend = gst_compositor_blend_abgr;
626       self->overlay = gst_compositor_overlay_abgr;
627       self->fill_checker = gst_compositor_fill_checker_abgr;
628       self->fill_color = gst_compositor_fill_color_abgr;
629       ret = TRUE;
630       break;
631     case GST_VIDEO_FORMAT_RGBA:
632       self->blend = gst_compositor_blend_rgba;
633       self->overlay = gst_compositor_overlay_rgba;
634       self->fill_checker = gst_compositor_fill_checker_rgba;
635       self->fill_color = gst_compositor_fill_color_rgba;
636       ret = TRUE;
637       break;
638     case GST_VIDEO_FORMAT_Y444:
639       self->blend = gst_compositor_blend_y444;
640       self->overlay = self->blend;
641       self->fill_checker = gst_compositor_fill_checker_y444;
642       self->fill_color = gst_compositor_fill_color_y444;
643       ret = TRUE;
644       break;
645     case GST_VIDEO_FORMAT_Y42B:
646       self->blend = gst_compositor_blend_y42b;
647       self->overlay = self->blend;
648       self->fill_checker = gst_compositor_fill_checker_y42b;
649       self->fill_color = gst_compositor_fill_color_y42b;
650       ret = TRUE;
651       break;
652     case GST_VIDEO_FORMAT_YUY2:
653       self->blend = gst_compositor_blend_yuy2;
654       self->overlay = self->blend;
655       self->fill_checker = gst_compositor_fill_checker_yuy2;
656       self->fill_color = gst_compositor_fill_color_yuy2;
657       ret = TRUE;
658       break;
659     case GST_VIDEO_FORMAT_UYVY:
660       self->blend = gst_compositor_blend_uyvy;
661       self->overlay = self->blend;
662       self->fill_checker = gst_compositor_fill_checker_uyvy;
663       self->fill_color = gst_compositor_fill_color_uyvy;
664       ret = TRUE;
665       break;
666     case GST_VIDEO_FORMAT_YVYU:
667       self->blend = gst_compositor_blend_yvyu;
668       self->overlay = self->blend;
669       self->fill_checker = gst_compositor_fill_checker_yvyu;
670       self->fill_color = gst_compositor_fill_color_yvyu;
671       ret = TRUE;
672       break;
673     case GST_VIDEO_FORMAT_I420:
674       self->blend = gst_compositor_blend_i420;
675       self->overlay = self->blend;
676       self->fill_checker = gst_compositor_fill_checker_i420;
677       self->fill_color = gst_compositor_fill_color_i420;
678       ret = TRUE;
679       break;
680     case GST_VIDEO_FORMAT_YV12:
681       self->blend = gst_compositor_blend_yv12;
682       self->overlay = self->blend;
683       self->fill_checker = gst_compositor_fill_checker_yv12;
684       self->fill_color = gst_compositor_fill_color_yv12;
685       ret = TRUE;
686       break;
687     case GST_VIDEO_FORMAT_NV12:
688       self->blend = gst_compositor_blend_nv12;
689       self->overlay = self->blend;
690       self->fill_checker = gst_compositor_fill_checker_nv12;
691       self->fill_color = gst_compositor_fill_color_nv12;
692       ret = TRUE;
693       break;
694     case GST_VIDEO_FORMAT_NV21:
695       self->blend = gst_compositor_blend_nv21;
696       self->overlay = self->blend;
697       self->fill_checker = gst_compositor_fill_checker_nv21;
698       self->fill_color = gst_compositor_fill_color_nv21;
699       ret = TRUE;
700       break;
701     case GST_VIDEO_FORMAT_Y41B:
702       self->blend = gst_compositor_blend_y41b;
703       self->overlay = self->blend;
704       self->fill_checker = gst_compositor_fill_checker_y41b;
705       self->fill_color = gst_compositor_fill_color_y41b;
706       ret = TRUE;
707       break;
708     case GST_VIDEO_FORMAT_RGB:
709       self->blend = gst_compositor_blend_rgb;
710       self->overlay = self->blend;
711       self->fill_checker = gst_compositor_fill_checker_rgb;
712       self->fill_color = gst_compositor_fill_color_rgb;
713       ret = TRUE;
714       break;
715     case GST_VIDEO_FORMAT_BGR:
716       self->blend = gst_compositor_blend_bgr;
717       self->overlay = self->blend;
718       self->fill_checker = gst_compositor_fill_checker_bgr;
719       self->fill_color = gst_compositor_fill_color_bgr;
720       ret = TRUE;
721       break;
722     case GST_VIDEO_FORMAT_xRGB:
723       self->blend = gst_compositor_blend_xrgb;
724       self->overlay = self->blend;
725       self->fill_checker = gst_compositor_fill_checker_xrgb;
726       self->fill_color = gst_compositor_fill_color_xrgb;
727       ret = TRUE;
728       break;
729     case GST_VIDEO_FORMAT_xBGR:
730       self->blend = gst_compositor_blend_xbgr;
731       self->overlay = self->blend;
732       self->fill_checker = gst_compositor_fill_checker_xbgr;
733       self->fill_color = gst_compositor_fill_color_xbgr;
734       ret = TRUE;
735       break;
736     case GST_VIDEO_FORMAT_RGBx:
737       self->blend = gst_compositor_blend_rgbx;
738       self->overlay = self->blend;
739       self->fill_checker = gst_compositor_fill_checker_rgbx;
740       self->fill_color = gst_compositor_fill_color_rgbx;
741       ret = TRUE;
742       break;
743     case GST_VIDEO_FORMAT_BGRx:
744       self->blend = gst_compositor_blend_bgrx;
745       self->overlay = self->blend;
746       self->fill_checker = gst_compositor_fill_checker_bgrx;
747       self->fill_color = gst_compositor_fill_color_bgrx;
748       ret = TRUE;
749       break;
750     default:
751       break;
752   }
753
754   return ret;
755 }
756
757 static GstCaps *
758 _fixate_caps (GstAggregator * agg, GstCaps * caps)
759 {
760   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
761   GList *l;
762   gint best_width = -1, best_height = -1;
763   gint best_fps_n = -1, best_fps_d = -1;
764   gint par_n, par_d;
765   gdouble best_fps = 0.;
766   GstCaps *ret = NULL;
767   GstStructure *s;
768
769   ret = gst_caps_make_writable (caps);
770
771   /* we need this to calculate how large to make the output frame */
772   s = gst_caps_get_structure (ret, 0);
773   if (gst_structure_has_field (s, "pixel-aspect-ratio")) {
774     gst_structure_fixate_field_nearest_fraction (s, "pixel-aspect-ratio", 1, 1);
775     gst_structure_get_fraction (s, "pixel-aspect-ratio", &par_n, &par_d);
776   } else {
777     par_n = par_d = 1;
778   }
779
780   GST_OBJECT_LOCK (vagg);
781   for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
782     GstVideoAggregatorPad *vaggpad = l->data;
783     GstCompositorPad *compositor_pad = GST_COMPOSITOR_PAD (vaggpad);
784     gint this_width, this_height;
785     gint width, height;
786     gint fps_n, fps_d;
787     gdouble cur_fps;
788
789     fps_n = GST_VIDEO_INFO_FPS_N (&vaggpad->info);
790     fps_d = GST_VIDEO_INFO_FPS_D (&vaggpad->info);
791     _mixer_pad_get_output_size (compositor_pad, par_n, par_d, &width, &height);
792
793     if (width == 0 || height == 0)
794       continue;
795
796     this_width = width + MAX (compositor_pad->xpos, 0);
797     this_height = height + MAX (compositor_pad->ypos, 0);
798
799     if (best_width < this_width)
800       best_width = this_width;
801     if (best_height < this_height)
802       best_height = this_height;
803
804     if (fps_d == 0)
805       cur_fps = 0.0;
806     else
807       gst_util_fraction_to_double (fps_n, fps_d, &cur_fps);
808
809     if (best_fps < cur_fps) {
810       best_fps = cur_fps;
811       best_fps_n = fps_n;
812       best_fps_d = fps_d;
813     }
814   }
815   GST_OBJECT_UNLOCK (vagg);
816
817   if (best_fps_n <= 0 || best_fps_d <= 0 || best_fps == 0.0) {
818     best_fps_n = 25;
819     best_fps_d = 1;
820     best_fps = 25.0;
821   }
822
823   gst_structure_fixate_field_nearest_int (s, "width", best_width);
824   gst_structure_fixate_field_nearest_int (s, "height", best_height);
825   gst_structure_fixate_field_nearest_fraction (s, "framerate", best_fps_n,
826       best_fps_d);
827   ret = gst_caps_fixate (ret);
828
829   return ret;
830 }
831
832 static gboolean
833 _negotiated_caps (GstAggregator * agg, GstCaps * caps)
834 {
835   GstVideoInfo v_info;
836
837   GST_DEBUG_OBJECT (agg, "Negotiated caps %" GST_PTR_FORMAT, caps);
838
839   if (!gst_video_info_from_caps (&v_info, caps))
840     return FALSE;
841
842   if (!set_functions (GST_COMPOSITOR (agg), &v_info)) {
843     GST_ERROR_OBJECT (agg, "Failed to setup vfuncs");
844     return FALSE;
845   }
846
847   return GST_AGGREGATOR_CLASS (parent_class)->negotiated_src_caps (agg, caps);
848 }
849
850 static gboolean
851 _should_draw_background (GstVideoAggregator * vagg, gboolean bg_transparent)
852 {
853   GstVideoRectangle bg_rect;
854   gboolean draw = TRUE;
855   GList *l;
856
857   bg_rect.x = bg_rect.y = 0;
858
859   GST_OBJECT_LOCK (vagg);
860   bg_rect.w = GST_VIDEO_INFO_WIDTH (&vagg->info);
861   bg_rect.h = GST_VIDEO_INFO_HEIGHT (&vagg->info);
862   /* Check if the background is completely obscured by a pad
863    * TODO: Also skip if it's obscured by a combination of pads */
864   for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
865     if (_pad_obscures_rectangle (vagg, l->data, bg_rect, bg_transparent)) {
866       draw = FALSE;
867       break;
868     }
869   }
870   GST_OBJECT_UNLOCK (vagg);
871   return draw;
872 }
873
874 static gboolean
875 _draw_background (GstVideoAggregator * vagg, GstVideoFrame * outframe,
876     BlendFunction * composite)
877 {
878   GstCompositor *comp = GST_COMPOSITOR (vagg);
879
880   *composite = comp->blend;
881   /* If one of the frames to be composited completely obscures the background,
882    * don't bother drawing the background at all. We can also always use the
883    * 'blend' BlendFunction in that case because it only changes if we have to
884    * overlay on top of a transparent background. */
885   if (!_should_draw_background (vagg,
886           comp->background == COMPOSITOR_BACKGROUND_TRANSPARENT))
887     return FALSE;
888
889   switch (comp->background) {
890     case COMPOSITOR_BACKGROUND_CHECKER:
891       comp->fill_checker (outframe);
892       break;
893     case COMPOSITOR_BACKGROUND_BLACK:
894       comp->fill_color (outframe, 16, 128, 128);
895       break;
896     case COMPOSITOR_BACKGROUND_WHITE:
897       comp->fill_color (outframe, 240, 128, 128);
898       break;
899     case COMPOSITOR_BACKGROUND_TRANSPARENT:
900     {
901       guint i, plane, num_planes, height;
902
903       num_planes = GST_VIDEO_FRAME_N_PLANES (outframe);
904       for (plane = 0; plane < num_planes; ++plane) {
905         guint8 *pdata;
906         gsize rowsize, plane_stride;
907
908         pdata = GST_VIDEO_FRAME_PLANE_DATA (outframe, plane);
909         plane_stride = GST_VIDEO_FRAME_PLANE_STRIDE (outframe, plane);
910         rowsize = GST_VIDEO_FRAME_COMP_WIDTH (outframe, plane)
911             * GST_VIDEO_FRAME_COMP_PSTRIDE (outframe, plane);
912         height = GST_VIDEO_FRAME_COMP_HEIGHT (outframe, plane);
913         for (i = 0; i < height; ++i) {
914           memset (pdata, 0, rowsize);
915           pdata += plane_stride;
916         }
917       }
918       /* use overlay to keep background transparent */
919       *composite = comp->overlay;
920       break;
921     }
922   }
923
924   return TRUE;
925 }
926
927 static gboolean
928 frames_can_copy (const GstVideoFrame * frame1, const GstVideoFrame * frame2)
929 {
930   if (GST_VIDEO_FRAME_FORMAT (frame1) != GST_VIDEO_FRAME_FORMAT (frame2))
931     return FALSE;
932   if (GST_VIDEO_FRAME_HEIGHT (frame1) != GST_VIDEO_FRAME_HEIGHT (frame2))
933     return FALSE;
934   if (GST_VIDEO_FRAME_WIDTH (frame1) != GST_VIDEO_FRAME_WIDTH (frame2))
935     return FALSE;
936   return TRUE;
937 }
938
939 static GstFlowReturn
940 gst_compositor_aggregate_frames (GstVideoAggregator * vagg, GstBuffer * outbuf)
941 {
942   GList *l;
943   BlendFunction composite;
944   GstVideoFrame out_frame, *outframe;
945   gboolean drew_background;
946   guint drawn_pads = 0;
947
948   if (!gst_video_frame_map (&out_frame, &vagg->info, outbuf, GST_MAP_WRITE)) {
949     GST_WARNING_OBJECT (vagg, "Could not map output buffer");
950     return GST_FLOW_ERROR;
951   }
952
953   outframe = &out_frame;
954   drew_background = _draw_background (vagg, outframe, &composite);
955
956   GST_OBJECT_LOCK (vagg);
957   for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
958     GstVideoAggregatorPad *pad = l->data;
959     GstCompositorPad *compo_pad = GST_COMPOSITOR_PAD (pad);
960     GstVideoFrame *prepared_frame =
961         gst_video_aggregator_pad_get_prepared_frame (pad);
962     GstCompositorBlendMode blend_mode = COMPOSITOR_BLEND_MODE_OVER;
963
964     switch (compo_pad->op) {
965       case COMPOSITOR_OPERATOR_SOURCE:
966         blend_mode = COMPOSITOR_BLEND_MODE_SOURCE;
967         break;
968       case COMPOSITOR_OPERATOR_OVER:
969         blend_mode = COMPOSITOR_BLEND_MODE_OVER;
970         break;
971       case COMPOSITOR_OPERATOR_ADD:
972         blend_mode = COMPOSITOR_BLEND_MODE_ADD;
973         break;
974       default:
975         g_assert_not_reached ();
976         break;
977     }
978
979     if (prepared_frame != NULL) {
980       /* If this is the first pad we're drawing, and we didn't draw the
981        * background, and @prepared_frame has the same format, height, and width
982        * as @outframe, then we can just copy it as-is. Subsequent pads (if any)
983        * will be composited on top of it. */
984       if (drawn_pads == 0 && !drew_background &&
985           frames_can_copy (prepared_frame, outframe))
986         gst_video_frame_copy (outframe, prepared_frame);
987       else
988         composite (prepared_frame,
989             compo_pad->xpos,
990             compo_pad->ypos, compo_pad->alpha, outframe, blend_mode);
991       drawn_pads++;
992     }
993   }
994   GST_OBJECT_UNLOCK (vagg);
995
996   gst_video_frame_unmap (outframe);
997
998   return GST_FLOW_OK;
999 }
1000
1001 static GstPad *
1002 gst_compositor_request_new_pad (GstElement * element, GstPadTemplate * templ,
1003     const gchar * req_name, const GstCaps * caps)
1004 {
1005   GstPad *newpad;
1006
1007   newpad = (GstPad *)
1008       GST_ELEMENT_CLASS (parent_class)->request_new_pad (element,
1009       templ, req_name, caps);
1010
1011   if (newpad == NULL)
1012     goto could_not_create;
1013
1014   gst_child_proxy_child_added (GST_CHILD_PROXY (element), G_OBJECT (newpad),
1015       GST_OBJECT_NAME (newpad));
1016
1017   return newpad;
1018
1019 could_not_create:
1020   {
1021     GST_DEBUG_OBJECT (element, "could not create/add pad");
1022     return NULL;
1023   }
1024 }
1025
1026 static void
1027 gst_compositor_release_pad (GstElement * element, GstPad * pad)
1028 {
1029   GstCompositor *compositor;
1030
1031   compositor = GST_COMPOSITOR (element);
1032
1033   GST_DEBUG_OBJECT (compositor, "release pad %s:%s", GST_DEBUG_PAD_NAME (pad));
1034
1035   gst_child_proxy_child_removed (GST_CHILD_PROXY (compositor), G_OBJECT (pad),
1036       GST_OBJECT_NAME (pad));
1037
1038   GST_ELEMENT_CLASS (parent_class)->release_pad (element, pad);
1039 }
1040
1041 static gboolean
1042 _sink_query (GstAggregator * agg, GstAggregatorPad * bpad, GstQuery * query)
1043 {
1044   switch (GST_QUERY_TYPE (query)) {
1045     case GST_QUERY_ALLOCATION:{
1046       GstCaps *caps;
1047       GstVideoInfo info;
1048       GstBufferPool *pool;
1049       guint size;
1050       GstStructure *structure;
1051
1052       gst_query_parse_allocation (query, &caps, NULL);
1053
1054       if (caps == NULL)
1055         return FALSE;
1056
1057       if (!gst_video_info_from_caps (&info, caps))
1058         return FALSE;
1059
1060       size = GST_VIDEO_INFO_SIZE (&info);
1061
1062       pool = gst_video_buffer_pool_new ();
1063
1064       structure = gst_buffer_pool_get_config (pool);
1065       gst_buffer_pool_config_set_params (structure, caps, size, 0, 0);
1066
1067       if (!gst_buffer_pool_set_config (pool, structure)) {
1068         gst_object_unref (pool);
1069         return FALSE;
1070       }
1071
1072       gst_query_add_allocation_pool (query, pool, size, 0, 0);
1073       gst_object_unref (pool);
1074       gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
1075
1076       return TRUE;
1077     }
1078     default:
1079       return GST_AGGREGATOR_CLASS (parent_class)->sink_query (agg, bpad, query);
1080   }
1081 }
1082
1083 /* GObject boilerplate */
1084 static void
1085 gst_compositor_class_init (GstCompositorClass * klass)
1086 {
1087   GObjectClass *gobject_class = (GObjectClass *) klass;
1088   GstElementClass *gstelement_class = (GstElementClass *) klass;
1089   GstVideoAggregatorClass *videoaggregator_class =
1090       (GstVideoAggregatorClass *) klass;
1091   GstAggregatorClass *agg_class = (GstAggregatorClass *) klass;
1092
1093   gobject_class->get_property = gst_compositor_get_property;
1094   gobject_class->set_property = gst_compositor_set_property;
1095
1096   gstelement_class->request_new_pad =
1097       GST_DEBUG_FUNCPTR (gst_compositor_request_new_pad);
1098   gstelement_class->release_pad =
1099       GST_DEBUG_FUNCPTR (gst_compositor_release_pad);
1100   agg_class->sink_query = _sink_query;
1101   agg_class->fixate_src_caps = _fixate_caps;
1102   agg_class->negotiated_src_caps = _negotiated_caps;
1103   videoaggregator_class->aggregate_frames = gst_compositor_aggregate_frames;
1104
1105   g_object_class_install_property (gobject_class, PROP_BACKGROUND,
1106       g_param_spec_enum ("background", "Background", "Background type",
1107           GST_TYPE_COMPOSITOR_BACKGROUND,
1108           DEFAULT_BACKGROUND, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1109
1110   gst_element_class_add_static_pad_template_with_gtype (gstelement_class,
1111       &src_factory, GST_TYPE_AGGREGATOR_PAD);
1112   gst_element_class_add_static_pad_template_with_gtype (gstelement_class,
1113       &sink_factory, GST_TYPE_COMPOSITOR_PAD);
1114
1115   gst_element_class_set_static_metadata (gstelement_class, "Compositor",
1116       "Filter/Editor/Video/Compositor",
1117       "Composite multiple video streams", "Wim Taymans <wim@fluendo.com>, "
1118       "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
1119
1120   gst_type_mark_as_plugin_api (GST_TYPE_COMPOSITOR_PAD, 0);
1121   gst_type_mark_as_plugin_api (GST_TYPE_COMPOSITOR_OPERATOR, 0);
1122   gst_type_mark_as_plugin_api (GST_TYPE_COMPOSITOR_BACKGROUND, 0);
1123 }
1124
1125 static void
1126 gst_compositor_init (GstCompositor * self)
1127 {
1128   /* initialize variables */
1129   self->background = DEFAULT_BACKGROUND;
1130 }
1131
1132 /* GstChildProxy implementation */
1133 static GObject *
1134 gst_compositor_child_proxy_get_child_by_index (GstChildProxy * child_proxy,
1135     guint index)
1136 {
1137   GstCompositor *compositor = GST_COMPOSITOR (child_proxy);
1138   GObject *obj = NULL;
1139
1140   GST_OBJECT_LOCK (compositor);
1141   obj = g_list_nth_data (GST_ELEMENT_CAST (compositor)->sinkpads, index);
1142   if (obj)
1143     gst_object_ref (obj);
1144   GST_OBJECT_UNLOCK (compositor);
1145
1146   return obj;
1147 }
1148
1149 static guint
1150 gst_compositor_child_proxy_get_children_count (GstChildProxy * child_proxy)
1151 {
1152   guint count = 0;
1153   GstCompositor *compositor = GST_COMPOSITOR (child_proxy);
1154
1155   GST_OBJECT_LOCK (compositor);
1156   count = GST_ELEMENT_CAST (compositor)->numsinkpads;
1157   GST_OBJECT_UNLOCK (compositor);
1158   GST_INFO_OBJECT (compositor, "Children Count: %d", count);
1159
1160   return count;
1161 }
1162
1163 static void
1164 gst_compositor_child_proxy_init (gpointer g_iface, gpointer iface_data)
1165 {
1166   GstChildProxyInterface *iface = g_iface;
1167
1168   iface->get_child_by_index = gst_compositor_child_proxy_get_child_by_index;
1169   iface->get_children_count = gst_compositor_child_proxy_get_children_count;
1170 }
1171
1172 /* Element registration */
1173 static gboolean
1174 plugin_init (GstPlugin * plugin)
1175 {
1176   GST_DEBUG_CATEGORY_INIT (gst_compositor_debug, "compositor", 0, "compositor");
1177
1178   gst_compositor_init_blend ();
1179
1180   return gst_element_register (plugin, "compositor", GST_RANK_PRIMARY + 1,
1181       GST_TYPE_COMPOSITOR);
1182 }
1183
1184 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1185     GST_VERSION_MINOR,
1186     compositor,
1187     "Compositor", plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME,
1188     GST_PACKAGE_ORIGIN)