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