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
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 GST_TYPE_COMPOSITOR_BACKGROUND (gst_compositor_background_get_type())
146 static GType
gst_compositor_background_get_type(void)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
gst_compositor_sizing_policy_get_type(void)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
gst_compositor_pad_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)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
gst_compositor_pad_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)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
_mixer_pad_get_output_size(GstCompositor * comp,GstCompositorPad * comp_pad,gint out_par_n,gint out_par_d,gint * width,gint * height,gint * x_offset,gint * y_offset)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 /* Test whether rectangle2 contains rectangle 1 (geometrically) */
426 static gboolean
is_rectangle_contained(const GstVideoRectangle rect1,const GstVideoRectangle rect2)427 is_rectangle_contained (const GstVideoRectangle rect1,
428 const GstVideoRectangle rect2)
429 {
430 if ((rect2.x <= rect1.x) && (rect2.y <= rect1.y) &&
431 ((rect2.x + rect2.w) >= (rect1.x + rect1.w)) &&
432 ((rect2.y + rect2.h) >= (rect1.y + rect1.h)))
433 return TRUE;
434 return FALSE;
435 }
436
437 static GstVideoRectangle
clamp_rectangle(gint x,gint y,gint w,gint h,gint outer_width,gint outer_height)438 clamp_rectangle (gint x, gint y, gint w, gint h, gint outer_width,
439 gint outer_height)
440 {
441 gint x2 = x + w;
442 gint y2 = y + h;
443 GstVideoRectangle clamped;
444
445 /* Clamp the x/y coordinates of this frame to the output boundaries to cover
446 * the case where (say, with negative xpos/ypos or w/h greater than the output
447 * size) the non-obscured portion of the frame could be outside the bounds of
448 * the video itself and hence not visible at all */
449 clamped.x = CLAMP (x, 0, outer_width);
450 clamped.y = CLAMP (y, 0, outer_height);
451 clamped.w = CLAMP (x2, 0, outer_width) - clamped.x;
452 clamped.h = CLAMP (y2, 0, outer_height) - clamped.y;
453
454 return clamped;
455 }
456
457 /* Call this with the lock taken */
458 static gboolean
_pad_obscures_rectangle(GstVideoAggregator * vagg,GstVideoAggregatorPad * pad,const GstVideoRectangle rect)459 _pad_obscures_rectangle (GstVideoAggregator * vagg, GstVideoAggregatorPad * pad,
460 const GstVideoRectangle rect)
461 {
462 GstVideoRectangle pad_rect;
463 GstCompositorPad *cpad = GST_COMPOSITOR_PAD (pad);
464 GstStructure *converter_config = NULL;
465 gboolean fill_border = TRUE;
466 guint32 border_argb = 0xff000000;
467 gint x_offset, y_offset;
468
469 /* No buffer to obscure the rectangle with */
470 if (!gst_video_aggregator_pad_has_current_buffer (pad))
471 return FALSE;
472
473 /* Can't obscure if we introduce alpha or if the format has an alpha
474 * component as we'd have to inspect every pixel to know if the frame is
475 * opaque, so assume it doesn't obscure
476 */
477 if (cpad->alpha != 1.0 || GST_VIDEO_INFO_HAS_ALPHA (&pad->info))
478 return FALSE;
479
480 /* If a converter-config is set and it is either configured to not fill any
481 * borders, or configured to use a non-opaque color, then we have to handle
482 * the pad as potentially containing transparency */
483 g_object_get (pad, "converter-config", &converter_config, NULL);
484 if (converter_config) {
485 gst_structure_get (converter_config, GST_VIDEO_CONVERTER_OPT_BORDER_ARGB,
486 G_TYPE_UINT, &border_argb, NULL);
487 gst_structure_get (converter_config, GST_VIDEO_CONVERTER_OPT_FILL_BORDER,
488 G_TYPE_BOOLEAN, &fill_border, NULL);
489 }
490 gst_clear_structure (&converter_config);
491 if (!fill_border || (border_argb & 0xff000000) != 0xff000000)
492 return FALSE;
493
494 pad_rect.x = cpad->xpos;
495 pad_rect.y = cpad->ypos;
496 /* Handle pixel and display aspect ratios to find the actual size */
497 _mixer_pad_get_output_size (GST_COMPOSITOR (vagg), cpad,
498 GST_VIDEO_INFO_PAR_N (&vagg->info), GST_VIDEO_INFO_PAR_D (&vagg->info),
499 &(pad_rect.w), &(pad_rect.h), &x_offset, &y_offset);
500 pad_rect.x += x_offset;
501 pad_rect.y += y_offset;
502
503 if (!is_rectangle_contained (rect, pad_rect))
504 return FALSE;
505
506 GST_DEBUG_OBJECT (pad, "Pad %s %ix%i@(%i,%i) obscures rect %ix%i@(%i,%i)",
507 GST_PAD_NAME (pad), pad_rect.w, pad_rect.h, pad_rect.x, pad_rect.y,
508 rect.w, rect.h, rect.x, rect.y);
509
510 return TRUE;
511 }
512
513 static void
gst_compositor_pad_prepare_frame_start(GstVideoAggregatorPad * pad,GstVideoAggregator * vagg,GstBuffer * buffer,GstVideoFrame * prepared_frame)514 gst_compositor_pad_prepare_frame_start (GstVideoAggregatorPad * pad,
515 GstVideoAggregator * vagg, GstBuffer * buffer,
516 GstVideoFrame * prepared_frame)
517 {
518 GstCompositorPad *cpad = GST_COMPOSITOR_PAD (pad);
519 gint width, height;
520 gboolean frame_obscured = FALSE;
521 GList *l;
522 /* The rectangle representing this frame, clamped to the video's boundaries.
523 * Due to the clamping, this is different from the frame width/height above. */
524 GstVideoRectangle frame_rect;
525
526 /* There's three types of width/height here:
527 * 1. GST_VIDEO_FRAME_WIDTH/HEIGHT:
528 * The frame width/height (same as pad->info.height/width;
529 * see gst_video_frame_map())
530 * 2. cpad->width/height:
531 * The optional pad property for scaling the frame (if zero, the video is
532 * left unscaled)
533 * 3. conversion_info.width/height:
534 * Equal to cpad->width/height if it's set, otherwise it's the pad
535 * width/height. See ->set_info()
536 * */
537
538 _mixer_pad_get_output_size (GST_COMPOSITOR (vagg), cpad,
539 GST_VIDEO_INFO_PAR_N (&vagg->info), GST_VIDEO_INFO_PAR_D (&vagg->info),
540 &width, &height, &cpad->x_offset, &cpad->y_offset);
541
542 if (cpad->alpha == 0.0) {
543 GST_DEBUG_OBJECT (pad, "Pad has alpha 0.0, not converting frame");
544 return;
545 }
546
547 if (gst_aggregator_pad_is_inactive (GST_AGGREGATOR_PAD (pad)))
548 return;
549
550 frame_rect = clamp_rectangle (cpad->xpos + cpad->x_offset,
551 cpad->ypos + cpad->y_offset, width, height,
552 GST_VIDEO_INFO_WIDTH (&vagg->info), GST_VIDEO_INFO_HEIGHT (&vagg->info));
553
554 if (frame_rect.w == 0 || frame_rect.h == 0) {
555 GST_DEBUG_OBJECT (pad, "Resulting frame is zero-width or zero-height "
556 "(w: %i, h: %i), skipping", frame_rect.w, frame_rect.h);
557 return;
558 }
559
560 GST_OBJECT_LOCK (vagg);
561 /* Check if this frame is obscured by a higher-zorder frame
562 * TODO: Also skip a frame if it's obscured by a combination of
563 * higher-zorder frames */
564 l = g_list_find (GST_ELEMENT (vagg)->sinkpads, pad);
565 /* The pad might've just been removed */
566 if (l)
567 l = l->next;
568 for (; l; l = l->next) {
569 if (_pad_obscures_rectangle (vagg, l->data, frame_rect)) {
570 frame_obscured = TRUE;
571 break;
572 }
573 }
574 GST_OBJECT_UNLOCK (vagg);
575
576 if (frame_obscured)
577 return;
578
579 GST_VIDEO_AGGREGATOR_PAD_CLASS
580 (gst_compositor_pad_parent_class)->prepare_frame_start (pad, vagg, buffer,
581 prepared_frame);
582 }
583
584 static void
gst_compositor_pad_create_conversion_info(GstVideoAggregatorConvertPad * pad,GstVideoAggregator * vagg,GstVideoInfo * conversion_info)585 gst_compositor_pad_create_conversion_info (GstVideoAggregatorConvertPad * pad,
586 GstVideoAggregator * vagg, GstVideoInfo * conversion_info)
587 {
588 GstCompositorPad *cpad = GST_COMPOSITOR_PAD (pad);
589 gint width, height;
590 gint x_offset, y_offset;
591
592 GST_VIDEO_AGGREGATOR_CONVERT_PAD_CLASS
593 (gst_compositor_pad_parent_class)->create_conversion_info (pad, vagg,
594 conversion_info);
595 if (!conversion_info->finfo)
596 return;
597
598 _mixer_pad_get_output_size (GST_COMPOSITOR (vagg), cpad,
599 GST_VIDEO_INFO_PAR_N (&vagg->info), GST_VIDEO_INFO_PAR_D (&vagg->info),
600 &width, &height, &x_offset, &y_offset);
601
602 /* The only thing that can change here is the width
603 * and height, otherwise set_info would've been called */
604 if (GST_VIDEO_INFO_WIDTH (conversion_info) != width ||
605 GST_VIDEO_INFO_HEIGHT (conversion_info) != height) {
606 GstVideoInfo tmp_info;
607
608 /* Initialize with the wanted video format and our original width and
609 * height as we don't want to rescale. Then copy over the wanted
610 * colorimetry, and chroma-site and our current pixel-aspect-ratio
611 * and other relevant fields.
612 */
613 gst_video_info_set_format (&tmp_info,
614 GST_VIDEO_INFO_FORMAT (conversion_info), width, height);
615 tmp_info.chroma_site = conversion_info->chroma_site;
616 tmp_info.colorimetry = conversion_info->colorimetry;
617 tmp_info.par_n = conversion_info->par_n;
618 tmp_info.par_d = conversion_info->par_d;
619 tmp_info.fps_n = conversion_info->fps_n;
620 tmp_info.fps_d = conversion_info->fps_d;
621 tmp_info.flags = conversion_info->flags;
622 tmp_info.interlace_mode = conversion_info->interlace_mode;
623
624 *conversion_info = tmp_info;
625 }
626 }
627
628 static void
gst_compositor_pad_class_init(GstCompositorPadClass * klass)629 gst_compositor_pad_class_init (GstCompositorPadClass * klass)
630 {
631 GObjectClass *gobject_class = (GObjectClass *) klass;
632 GstVideoAggregatorPadClass *vaggpadclass =
633 (GstVideoAggregatorPadClass *) klass;
634 GstVideoAggregatorConvertPadClass *vaggcpadclass =
635 (GstVideoAggregatorConvertPadClass *) klass;
636
637 gobject_class->set_property = gst_compositor_pad_set_property;
638 gobject_class->get_property = gst_compositor_pad_get_property;
639
640 g_object_class_install_property (gobject_class, PROP_PAD_XPOS,
641 g_param_spec_int ("xpos", "X Position", "X Position of the picture",
642 G_MININT, G_MAXINT, DEFAULT_PAD_XPOS,
643 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
644 g_object_class_install_property (gobject_class, PROP_PAD_YPOS,
645 g_param_spec_int ("ypos", "Y Position", "Y Position of the picture",
646 G_MININT, G_MAXINT, DEFAULT_PAD_YPOS,
647 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
648 g_object_class_install_property (gobject_class, PROP_PAD_WIDTH,
649 g_param_spec_int ("width", "Width", "Width of the picture",
650 G_MININT, G_MAXINT, DEFAULT_PAD_WIDTH,
651 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
652 g_object_class_install_property (gobject_class, PROP_PAD_HEIGHT,
653 g_param_spec_int ("height", "Height", "Height of the picture",
654 G_MININT, G_MAXINT, DEFAULT_PAD_HEIGHT,
655 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
656 g_object_class_install_property (gobject_class, PROP_PAD_ALPHA,
657 g_param_spec_double ("alpha", "Alpha", "Alpha of the picture", 0.0, 1.0,
658 DEFAULT_PAD_ALPHA,
659 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
660 g_object_class_install_property (gobject_class, PROP_PAD_OPERATOR,
661 g_param_spec_enum ("operator", "Operator",
662 "Blending operator to use for blending this pad over the previous ones",
663 GST_TYPE_COMPOSITOR_OPERATOR, DEFAULT_PAD_OPERATOR,
664 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
665
666 /**
667 * GstCompositorPad:sizing-policy:
668 *
669 * Specifies sizing policy to use. Depending on selected sizing policy,
670 * scaled image might not fully cover the configured target rectangle area
671 * (e.g., "keep-aspect-ratio"). In that case, any uncovered area will be
672 * filled with background unless the uncovered area is drawn by other image.
673 *
674 * Since: 1.20
675 */
676 g_object_class_install_property (gobject_class, PROP_PAD_SIZING_POLICY,
677 g_param_spec_enum ("sizing-policy", "Sizing policy",
678 "Sizing policy to use for image scaling",
679 GST_TYPE_COMPOSITOR_SIZING_POLICY, DEFAULT_PAD_SIZING_POLICY,
680 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
681
682 vaggpadclass->prepare_frame_start =
683 GST_DEBUG_FUNCPTR (gst_compositor_pad_prepare_frame_start);
684
685 vaggcpadclass->create_conversion_info =
686 GST_DEBUG_FUNCPTR (gst_compositor_pad_create_conversion_info);
687
688 gst_type_mark_as_plugin_api (GST_TYPE_COMPOSITOR_SIZING_POLICY, 0);
689 }
690
691 static void
gst_compositor_pad_init(GstCompositorPad * compo_pad)692 gst_compositor_pad_init (GstCompositorPad * compo_pad)
693 {
694 compo_pad->xpos = DEFAULT_PAD_XPOS;
695 compo_pad->ypos = DEFAULT_PAD_YPOS;
696 compo_pad->alpha = DEFAULT_PAD_ALPHA;
697 compo_pad->op = DEFAULT_PAD_OPERATOR;
698 compo_pad->width = DEFAULT_PAD_WIDTH;
699 compo_pad->height = DEFAULT_PAD_HEIGHT;
700 compo_pad->sizing_policy = DEFAULT_PAD_SIZING_POLICY;
701 }
702
703
704 /* GstCompositor */
705 #define DEFAULT_BACKGROUND COMPOSITOR_BACKGROUND_CHECKER
706 #define DEFAULT_ZERO_SIZE_IS_UNSCALED TRUE
707 #define DEFAULT_MAX_THREADS 0
708
709 enum
710 {
711 PROP_0,
712 PROP_BACKGROUND,
713 PROP_ZERO_SIZE_IS_UNSCALED,
714 PROP_MAX_THREADS,
715 PROP_IGNORE_INACTIVE_PADS,
716 };
717
718 static void
gst_compositor_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)719 gst_compositor_get_property (GObject * object,
720 guint prop_id, GValue * value, GParamSpec * pspec)
721 {
722 GstCompositor *self = GST_COMPOSITOR (object);
723
724 switch (prop_id) {
725 case PROP_BACKGROUND:
726 g_value_set_enum (value, self->background);
727 break;
728 case PROP_ZERO_SIZE_IS_UNSCALED:
729 g_value_set_boolean (value, self->zero_size_is_unscaled);
730 break;
731 case PROP_MAX_THREADS:
732 g_value_set_uint (value, self->max_threads);
733 break;
734 case PROP_IGNORE_INACTIVE_PADS:
735 g_value_set_boolean (value,
736 gst_aggregator_get_ignore_inactive_pads (GST_AGGREGATOR (object)));
737 break;
738 default:
739 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
740 break;
741 }
742 }
743
744 static void
gst_compositor_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)745 gst_compositor_set_property (GObject * object,
746 guint prop_id, const GValue * value, GParamSpec * pspec)
747 {
748 GstCompositor *self = GST_COMPOSITOR (object);
749
750 switch (prop_id) {
751 case PROP_BACKGROUND:
752 self->background = g_value_get_enum (value);
753 break;
754 case PROP_ZERO_SIZE_IS_UNSCALED:
755 self->zero_size_is_unscaled = g_value_get_boolean (value);
756 break;
757 case PROP_MAX_THREADS:
758 self->max_threads = g_value_get_uint (value);
759 break;
760 case PROP_IGNORE_INACTIVE_PADS:
761 gst_aggregator_set_ignore_inactive_pads (GST_AGGREGATOR (object),
762 g_value_get_boolean (value));
763 break;
764 default:
765 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
766 break;
767 }
768 }
769
770 #define gst_compositor_parent_class parent_class
771 G_DEFINE_TYPE_WITH_CODE (GstCompositor, gst_compositor,
772 GST_TYPE_VIDEO_AGGREGATOR, G_IMPLEMENT_INTERFACE (GST_TYPE_CHILD_PROXY,
773 gst_compositor_child_proxy_init));
774 GST_ELEMENT_REGISTER_DEFINE (compositor, "compositor", GST_RANK_PRIMARY + 1,
775 GST_TYPE_COMPOSITOR);
776
777 static gboolean
set_functions(GstCompositor * self,const GstVideoInfo * info)778 set_functions (GstCompositor * self, const GstVideoInfo * info)
779 {
780 gboolean ret = FALSE;
781
782 self->blend = NULL;
783 self->overlay = NULL;
784 self->fill_checker = NULL;
785 self->fill_color = NULL;
786
787 switch (GST_VIDEO_INFO_FORMAT (info)) {
788 case GST_VIDEO_FORMAT_AYUV:
789 self->blend = gst_compositor_blend_ayuv;
790 self->overlay = gst_compositor_overlay_ayuv;
791 self->fill_checker = gst_compositor_fill_checker_ayuv;
792 self->fill_color = gst_compositor_fill_color_ayuv;
793 ret = TRUE;
794 break;
795 case GST_VIDEO_FORMAT_VUYA:
796 self->blend = gst_compositor_blend_vuya;
797 self->overlay = gst_compositor_overlay_vuya;
798 self->fill_checker = gst_compositor_fill_checker_vuya;
799 self->fill_color = gst_compositor_fill_color_vuya;
800 ret = TRUE;
801 break;
802 case GST_VIDEO_FORMAT_ARGB:
803 self->blend = gst_compositor_blend_argb;
804 self->overlay = gst_compositor_overlay_argb;
805 self->fill_checker = gst_compositor_fill_checker_argb;
806 self->fill_color = gst_compositor_fill_color_argb;
807 ret = TRUE;
808 break;
809 case GST_VIDEO_FORMAT_BGRA:
810 self->blend = gst_compositor_blend_bgra;
811 self->overlay = gst_compositor_overlay_bgra;
812 self->fill_checker = gst_compositor_fill_checker_bgra;
813 self->fill_color = gst_compositor_fill_color_bgra;
814 ret = TRUE;
815 break;
816 case GST_VIDEO_FORMAT_ABGR:
817 self->blend = gst_compositor_blend_abgr;
818 self->overlay = gst_compositor_overlay_abgr;
819 self->fill_checker = gst_compositor_fill_checker_abgr;
820 self->fill_color = gst_compositor_fill_color_abgr;
821 ret = TRUE;
822 break;
823 case GST_VIDEO_FORMAT_RGBA:
824 self->blend = gst_compositor_blend_rgba;
825 self->overlay = gst_compositor_overlay_rgba;
826 self->fill_checker = gst_compositor_fill_checker_rgba;
827 self->fill_color = gst_compositor_fill_color_rgba;
828 ret = TRUE;
829 break;
830 case GST_VIDEO_FORMAT_Y444:
831 self->blend = gst_compositor_blend_y444;
832 self->overlay = self->blend;
833 self->fill_checker = gst_compositor_fill_checker_y444;
834 self->fill_color = gst_compositor_fill_color_y444;
835 ret = TRUE;
836 break;
837 case GST_VIDEO_FORMAT_Y42B:
838 self->blend = gst_compositor_blend_y42b;
839 self->overlay = self->blend;
840 self->fill_checker = gst_compositor_fill_checker_y42b;
841 self->fill_color = gst_compositor_fill_color_y42b;
842 ret = TRUE;
843 break;
844 case GST_VIDEO_FORMAT_YUY2:
845 self->blend = gst_compositor_blend_yuy2;
846 self->overlay = self->blend;
847 self->fill_checker = gst_compositor_fill_checker_yuy2;
848 self->fill_color = gst_compositor_fill_color_yuy2;
849 ret = TRUE;
850 break;
851 case GST_VIDEO_FORMAT_UYVY:
852 self->blend = gst_compositor_blend_uyvy;
853 self->overlay = self->blend;
854 self->fill_checker = gst_compositor_fill_checker_uyvy;
855 self->fill_color = gst_compositor_fill_color_uyvy;
856 ret = TRUE;
857 break;
858 case GST_VIDEO_FORMAT_YVYU:
859 self->blend = gst_compositor_blend_yvyu;
860 self->overlay = self->blend;
861 self->fill_checker = gst_compositor_fill_checker_yvyu;
862 self->fill_color = gst_compositor_fill_color_yvyu;
863 ret = TRUE;
864 break;
865 case GST_VIDEO_FORMAT_I420:
866 self->blend = gst_compositor_blend_i420;
867 self->overlay = self->blend;
868 self->fill_checker = gst_compositor_fill_checker_i420;
869 self->fill_color = gst_compositor_fill_color_i420;
870 ret = TRUE;
871 break;
872 case GST_VIDEO_FORMAT_YV12:
873 self->blend = gst_compositor_blend_yv12;
874 self->overlay = self->blend;
875 self->fill_checker = gst_compositor_fill_checker_yv12;
876 self->fill_color = gst_compositor_fill_color_yv12;
877 ret = TRUE;
878 break;
879 case GST_VIDEO_FORMAT_NV12:
880 self->blend = gst_compositor_blend_nv12;
881 self->overlay = self->blend;
882 self->fill_checker = gst_compositor_fill_checker_nv12;
883 self->fill_color = gst_compositor_fill_color_nv12;
884 ret = TRUE;
885 break;
886 case GST_VIDEO_FORMAT_NV21:
887 self->blend = gst_compositor_blend_nv21;
888 self->overlay = self->blend;
889 self->fill_checker = gst_compositor_fill_checker_nv21;
890 self->fill_color = gst_compositor_fill_color_nv21;
891 ret = TRUE;
892 break;
893 case GST_VIDEO_FORMAT_Y41B:
894 self->blend = gst_compositor_blend_y41b;
895 self->overlay = self->blend;
896 self->fill_checker = gst_compositor_fill_checker_y41b;
897 self->fill_color = gst_compositor_fill_color_y41b;
898 ret = TRUE;
899 break;
900 case GST_VIDEO_FORMAT_RGB:
901 self->blend = gst_compositor_blend_rgb;
902 self->overlay = self->blend;
903 self->fill_checker = gst_compositor_fill_checker_rgb;
904 self->fill_color = gst_compositor_fill_color_rgb;
905 ret = TRUE;
906 break;
907 case GST_VIDEO_FORMAT_BGR:
908 self->blend = gst_compositor_blend_bgr;
909 self->overlay = self->blend;
910 self->fill_checker = gst_compositor_fill_checker_bgr;
911 self->fill_color = gst_compositor_fill_color_bgr;
912 ret = TRUE;
913 break;
914 case GST_VIDEO_FORMAT_xRGB:
915 self->blend = gst_compositor_blend_xrgb;
916 self->overlay = self->blend;
917 self->fill_checker = gst_compositor_fill_checker_xrgb;
918 self->fill_color = gst_compositor_fill_color_xrgb;
919 ret = TRUE;
920 break;
921 case GST_VIDEO_FORMAT_xBGR:
922 self->blend = gst_compositor_blend_xbgr;
923 self->overlay = self->blend;
924 self->fill_checker = gst_compositor_fill_checker_xbgr;
925 self->fill_color = gst_compositor_fill_color_xbgr;
926 ret = TRUE;
927 break;
928 case GST_VIDEO_FORMAT_RGBx:
929 self->blend = gst_compositor_blend_rgbx;
930 self->overlay = self->blend;
931 self->fill_checker = gst_compositor_fill_checker_rgbx;
932 self->fill_color = gst_compositor_fill_color_rgbx;
933 ret = TRUE;
934 break;
935 case GST_VIDEO_FORMAT_BGRx:
936 self->blend = gst_compositor_blend_bgrx;
937 self->overlay = self->blend;
938 self->fill_checker = gst_compositor_fill_checker_bgrx;
939 self->fill_color = gst_compositor_fill_color_bgrx;
940 ret = TRUE;
941 break;
942 default:
943 break;
944 }
945
946 return ret;
947 }
948
949 static GstCaps *
_fixate_caps(GstAggregator * agg,GstCaps * caps)950 _fixate_caps (GstAggregator * agg, GstCaps * caps)
951 {
952 GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
953 GList *l;
954 gint best_width = -1, best_height = -1;
955 gint best_fps_n = -1, best_fps_d = -1;
956 gint par_n, par_d;
957 gdouble best_fps = 0.;
958 GstCaps *ret = NULL;
959 GstStructure *s;
960
961 ret = gst_caps_make_writable (caps);
962
963 /* we need this to calculate how large to make the output frame */
964 s = gst_caps_get_structure (ret, 0);
965 if (gst_structure_has_field (s, "pixel-aspect-ratio")) {
966 gst_structure_fixate_field_nearest_fraction (s, "pixel-aspect-ratio", 1, 1);
967 gst_structure_get_fraction (s, "pixel-aspect-ratio", &par_n, &par_d);
968 } else {
969 par_n = par_d = 1;
970 }
971
972 GST_OBJECT_LOCK (vagg);
973 for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
974 GstVideoAggregatorPad *vaggpad = l->data;
975 GstCompositorPad *compositor_pad = GST_COMPOSITOR_PAD (vaggpad);
976 gint this_width, this_height;
977 gint width, height;
978 gint fps_n, fps_d;
979 gdouble cur_fps;
980 gint x_offset;
981 gint y_offset;
982
983 if (gst_aggregator_pad_is_inactive (GST_AGGREGATOR_PAD (vaggpad)))
984 continue;
985
986 fps_n = GST_VIDEO_INFO_FPS_N (&vaggpad->info);
987 fps_d = GST_VIDEO_INFO_FPS_D (&vaggpad->info);
988 _mixer_pad_get_output_size (GST_COMPOSITOR (vagg), compositor_pad, par_n,
989 par_d, &width, &height, &x_offset, &y_offset);
990
991 if (width == 0 || height == 0)
992 continue;
993
994 /* {x,y}_offset represent padding size of each top and left area.
995 * To calculate total resolution, count bottom and right padding area
996 * as well here */
997 this_width = width + MAX (compositor_pad->xpos + 2 * x_offset, 0);
998 this_height = height + MAX (compositor_pad->ypos + 2 * y_offset, 0);
999
1000 if (best_width < this_width)
1001 best_width = this_width;
1002 if (best_height < this_height)
1003 best_height = this_height;
1004
1005 if (fps_d == 0)
1006 cur_fps = 0.0;
1007 else
1008 gst_util_fraction_to_double (fps_n, fps_d, &cur_fps);
1009
1010 if (best_fps < cur_fps) {
1011 best_fps = cur_fps;
1012 best_fps_n = fps_n;
1013 best_fps_d = fps_d;
1014 }
1015 }
1016 GST_OBJECT_UNLOCK (vagg);
1017
1018 if (best_fps_n <= 0 || best_fps_d <= 0 || best_fps == 0.0) {
1019 best_fps_n = 25;
1020 best_fps_d = 1;
1021 best_fps = 25.0;
1022 }
1023
1024 gst_structure_fixate_field_nearest_int (s, "width", best_width);
1025 gst_structure_fixate_field_nearest_int (s, "height", best_height);
1026 gst_structure_fixate_field_nearest_fraction (s, "framerate", best_fps_n,
1027 best_fps_d);
1028 ret = gst_caps_fixate (ret);
1029
1030 return ret;
1031 }
1032
1033 static void
gst_parallelized_task_thread_func(gpointer data)1034 gst_parallelized_task_thread_func (gpointer data)
1035 {
1036 GstParallelizedTaskRunner *runner = data;
1037 gint idx;
1038
1039 g_mutex_lock (&runner->lock);
1040 idx = runner->n_todo--;
1041 g_assert (runner->n_todo >= -1);
1042 g_mutex_unlock (&runner->lock);
1043
1044 g_assert (runner->func != NULL);
1045
1046 runner->func (runner->task_data[idx]);
1047 }
1048
1049 static void
gst_parallelized_task_runner_join(GstParallelizedTaskRunner * self)1050 gst_parallelized_task_runner_join (GstParallelizedTaskRunner * self)
1051 {
1052 gboolean joined = FALSE;
1053
1054 while (!joined) {
1055 g_mutex_lock (&self->lock);
1056 if (!(joined = gst_queue_array_is_empty (self->tasks))) {
1057 gpointer task = gst_queue_array_pop_head (self->tasks);
1058 g_mutex_unlock (&self->lock);
1059 gst_task_pool_join (self->pool, task);
1060 } else {
1061 g_mutex_unlock (&self->lock);
1062 }
1063 }
1064 }
1065
1066 static void
gst_parallelized_task_runner_free(GstParallelizedTaskRunner * self)1067 gst_parallelized_task_runner_free (GstParallelizedTaskRunner * self)
1068 {
1069 gst_parallelized_task_runner_join (self);
1070
1071 gst_queue_array_free (self->tasks);
1072 if (self->own_pool)
1073 gst_task_pool_cleanup (self->pool);
1074 gst_object_unref (self->pool);
1075 g_mutex_clear (&self->lock);
1076 g_free (self);
1077 }
1078
1079 static GstParallelizedTaskRunner *
gst_parallelized_task_runner_new(guint n_threads,GstTaskPool * pool,gboolean async_tasks)1080 gst_parallelized_task_runner_new (guint n_threads, GstTaskPool * pool,
1081 gboolean async_tasks)
1082 {
1083 GstParallelizedTaskRunner *self;
1084
1085 if (n_threads == 0)
1086 n_threads = g_get_num_processors ();
1087
1088 self = g_new0 (GstParallelizedTaskRunner, 1);
1089
1090 if (pool) {
1091 self->pool = g_object_ref (pool);
1092 self->own_pool = FALSE;
1093
1094 /* No reason to split up the work between more threads than the
1095 * pool can spawn */
1096 if (GST_IS_SHARED_TASK_POOL (pool))
1097 n_threads =
1098 MIN (n_threads,
1099 gst_shared_task_pool_get_max_threads (GST_SHARED_TASK_POOL (pool)));
1100 } else {
1101 self->pool = gst_shared_task_pool_new ();
1102 self->own_pool = TRUE;
1103 gst_shared_task_pool_set_max_threads (GST_SHARED_TASK_POOL (self->pool),
1104 n_threads);
1105 gst_task_pool_prepare (self->pool, NULL);
1106 }
1107
1108 self->tasks = gst_queue_array_new (n_threads);
1109
1110 self->n_threads = n_threads;
1111
1112 self->n_todo = -1;
1113 g_mutex_init (&self->lock);
1114
1115 /* Set when scheduling a job */
1116 self->func = NULL;
1117 self->task_data = NULL;
1118 self->async_tasks = async_tasks;
1119
1120 return self;
1121 }
1122
1123 static void
gst_parallelized_task_runner_finish(GstParallelizedTaskRunner * self)1124 gst_parallelized_task_runner_finish (GstParallelizedTaskRunner * self)
1125 {
1126 g_return_if_fail (self->func != NULL);
1127
1128 gst_parallelized_task_runner_join (self);
1129
1130 self->func = NULL;
1131 self->task_data = NULL;
1132 }
1133
1134 static void
gst_parallelized_task_runner_run(GstParallelizedTaskRunner * self,GstParallelizedTaskFunc func,gpointer * task_data)1135 gst_parallelized_task_runner_run (GstParallelizedTaskRunner * self,
1136 GstParallelizedTaskFunc func, gpointer * task_data)
1137 {
1138 guint n_threads = self->n_threads;
1139
1140 self->func = func;
1141 self->task_data = task_data;
1142
1143 if (n_threads > 1 || self->async_tasks) {
1144 guint i = 0;
1145 g_mutex_lock (&self->lock);
1146 self->n_todo = self->n_threads - 1;
1147 if (!self->async_tasks) {
1148 /* if not async, perform one of the functions in the current thread */
1149 self->n_todo--;
1150 i = 1;
1151 }
1152 for (; i < n_threads; i++) {
1153 gpointer task =
1154 gst_task_pool_push (self->pool, gst_parallelized_task_thread_func,
1155 self, NULL);
1156
1157 /* The return value of push() is nullable but NULL is only returned
1158 * with the shared task pool when gst_task_pool_prepare() has not been
1159 * called and would thus be a programming error that we should hard-fail
1160 * on.
1161 */
1162 g_assert (task != NULL);
1163 gst_queue_array_push_tail (self->tasks, task);
1164 }
1165 g_mutex_unlock (&self->lock);
1166 }
1167
1168 if (!self->async_tasks) {
1169 self->func (self->task_data[self->n_threads - 1]);
1170
1171 gst_parallelized_task_runner_finish (self);
1172 }
1173 }
1174
1175 static gboolean
_negotiated_caps(GstAggregator * agg,GstCaps * caps)1176 _negotiated_caps (GstAggregator * agg, GstCaps * caps)
1177 {
1178 GstCompositor *compositor = GST_COMPOSITOR (agg);
1179 GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
1180 GstVideoInfo v_info;
1181 guint n_threads;
1182
1183 GST_DEBUG_OBJECT (agg, "Negotiated caps %" GST_PTR_FORMAT, caps);
1184
1185 if (!gst_video_info_from_caps (&v_info, caps))
1186 return FALSE;
1187
1188 if (!set_functions (compositor, &v_info)) {
1189 GST_ERROR_OBJECT (agg, "Failed to setup vfuncs");
1190 return FALSE;
1191 }
1192
1193 if (compositor->max_threads == 0)
1194 n_threads = g_get_num_processors ();
1195 else
1196 n_threads = compositor->max_threads;
1197
1198 /* Magic number of 200 lines */
1199 if (GST_VIDEO_INFO_HEIGHT (&v_info) / n_threads < 200)
1200 n_threads = (GST_VIDEO_INFO_HEIGHT (&v_info) + 199) / 200;
1201 if (n_threads < 1)
1202 n_threads = 1;
1203
1204 /* XXX: implement better thread count change */
1205 if (compositor->blend_runner
1206 && compositor->blend_runner->n_threads != n_threads) {
1207 gst_parallelized_task_runner_free (compositor->blend_runner);
1208 compositor->blend_runner = NULL;
1209 }
1210 if (!compositor->blend_runner) {
1211 GstTaskPool *pool = gst_video_aggregator_get_execution_task_pool (vagg);
1212 compositor->blend_runner =
1213 gst_parallelized_task_runner_new (n_threads, pool, FALSE);
1214 gst_clear_object (&pool);
1215 }
1216
1217 return GST_AGGREGATOR_CLASS (parent_class)->negotiated_src_caps (agg, caps);
1218 }
1219
1220 static gboolean
_should_draw_background(GstVideoAggregator * vagg)1221 _should_draw_background (GstVideoAggregator * vagg)
1222 {
1223 GstVideoRectangle bg_rect;
1224 gboolean draw = TRUE;
1225 GList *l;
1226
1227 bg_rect.x = bg_rect.y = 0;
1228
1229 GST_OBJECT_LOCK (vagg);
1230 bg_rect.w = GST_VIDEO_INFO_WIDTH (&vagg->info);
1231 bg_rect.h = GST_VIDEO_INFO_HEIGHT (&vagg->info);
1232 /* Check if the background is completely obscured by a pad
1233 * TODO: Also skip if it's obscured by a combination of pads */
1234 for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
1235 if (gst_aggregator_pad_is_inactive (GST_AGGREGATOR_PAD (l->data)))
1236 continue;
1237
1238 if (_pad_obscures_rectangle (vagg, l->data, bg_rect)) {
1239 draw = FALSE;
1240 break;
1241 }
1242 }
1243 GST_OBJECT_UNLOCK (vagg);
1244 return draw;
1245 }
1246
1247 static gboolean
frames_can_copy(const GstVideoFrame * frame1,const GstVideoFrame * frame2)1248 frames_can_copy (const GstVideoFrame * frame1, const GstVideoFrame * frame2)
1249 {
1250 if (GST_VIDEO_FRAME_FORMAT (frame1) != GST_VIDEO_FRAME_FORMAT (frame2))
1251 return FALSE;
1252 if (GST_VIDEO_FRAME_HEIGHT (frame1) != GST_VIDEO_FRAME_HEIGHT (frame2))
1253 return FALSE;
1254 if (GST_VIDEO_FRAME_WIDTH (frame1) != GST_VIDEO_FRAME_WIDTH (frame2))
1255 return FALSE;
1256 return TRUE;
1257 }
1258
1259 struct CompositePadInfo
1260 {
1261 GstVideoFrame *prepared_frame;
1262 GstCompositorPad *pad;
1263 GstCompositorBlendMode blend_mode;
1264 };
1265
1266 struct CompositeTask
1267 {
1268 GstCompositor *compositor;
1269 GstVideoFrame *out_frame;
1270 guint dst_line_start;
1271 guint dst_line_end;
1272 gboolean draw_background;
1273 guint n_pads;
1274 struct CompositePadInfo *pads_info;
1275 };
1276
1277 static void
_draw_background(GstCompositor * comp,GstVideoFrame * outframe,guint y_start,guint y_end,BlendFunction * composite)1278 _draw_background (GstCompositor * comp, GstVideoFrame * outframe,
1279 guint y_start, guint y_end, BlendFunction * composite)
1280 {
1281 *composite = comp->blend;
1282
1283 switch (comp->background) {
1284 case COMPOSITOR_BACKGROUND_CHECKER:
1285 comp->fill_checker (outframe, y_start, y_end);
1286 break;
1287 case COMPOSITOR_BACKGROUND_BLACK:
1288 comp->fill_color (outframe, y_start, y_end, 16, 128, 128);
1289 break;
1290 case COMPOSITOR_BACKGROUND_WHITE:
1291 comp->fill_color (outframe, y_start, y_end, 240, 128, 128);
1292 break;
1293 case COMPOSITOR_BACKGROUND_TRANSPARENT:
1294 {
1295 guint i, plane, num_planes, height;
1296
1297 num_planes = GST_VIDEO_FRAME_N_PLANES (outframe);
1298 for (plane = 0; plane < num_planes; ++plane) {
1299 const GstVideoFormatInfo *info;
1300 gint comp[GST_VIDEO_MAX_COMPONENTS];
1301 guint8 *pdata;
1302 gsize rowsize, plane_stride;
1303 gint yoffset;
1304
1305 info = outframe->info.finfo;
1306 pdata = GST_VIDEO_FRAME_PLANE_DATA (outframe, plane);
1307 plane_stride = GST_VIDEO_FRAME_PLANE_STRIDE (outframe, plane);
1308
1309 gst_video_format_info_component (info, plane, comp);
1310 rowsize = GST_VIDEO_FRAME_COMP_WIDTH (outframe, comp[0])
1311 * GST_VIDEO_FRAME_COMP_PSTRIDE (outframe, comp[0]);
1312 height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, comp[0],
1313 (y_end - y_start));
1314
1315 yoffset = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, comp[0], y_start);
1316
1317 pdata += yoffset * plane_stride;
1318 for (i = 0; i < height; ++i) {
1319 memset (pdata, 0, rowsize);
1320 pdata += plane_stride;
1321 }
1322 }
1323 /* use overlay to keep background transparent */
1324 *composite = comp->overlay;
1325 break;
1326 }
1327 }
1328 }
1329
1330 static void
blend_pads(struct CompositeTask * comp)1331 blend_pads (struct CompositeTask *comp)
1332 {
1333 BlendFunction composite;
1334 guint i;
1335
1336 composite = comp->compositor->blend;
1337
1338 if (comp->draw_background) {
1339 _draw_background (comp->compositor, comp->out_frame, comp->dst_line_start,
1340 comp->dst_line_end, &composite);
1341 }
1342
1343 for (i = 0; i < comp->n_pads; i++) {
1344 composite (comp->pads_info[i].prepared_frame,
1345 comp->pads_info[i].pad->xpos + comp->pads_info[i].pad->x_offset,
1346 comp->pads_info[i].pad->ypos + comp->pads_info[i].pad->y_offset,
1347 comp->pads_info[i].pad->alpha, comp->out_frame, comp->dst_line_start,
1348 comp->dst_line_end, comp->pads_info[i].blend_mode);
1349 }
1350 }
1351
1352 static GstFlowReturn
gst_compositor_aggregate_frames(GstVideoAggregator * vagg,GstBuffer * outbuf)1353 gst_compositor_aggregate_frames (GstVideoAggregator * vagg, GstBuffer * outbuf)
1354 {
1355 GstCompositor *compositor = GST_COMPOSITOR (vagg);
1356 GList *l;
1357 GstVideoFrame out_frame, *outframe;
1358 gboolean draw_background;
1359 guint drawn_a_pad = FALSE;
1360 struct CompositePadInfo *pads_info;
1361 guint i, n_pads = 0;
1362
1363 if (!gst_video_frame_map (&out_frame, &vagg->info, outbuf, GST_MAP_WRITE)) {
1364 GST_WARNING_OBJECT (vagg, "Could not map output buffer");
1365 return GST_FLOW_ERROR;
1366 }
1367
1368 outframe = &out_frame;
1369
1370 /* If one of the frames to be composited completely obscures the background,
1371 * don't bother drawing the background at all. We can also always use the
1372 * 'blend' BlendFunction in that case because it only changes if we have to
1373 * overlay on top of a transparent background. */
1374 draw_background = _should_draw_background (vagg);
1375
1376 GST_OBJECT_LOCK (vagg);
1377 for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
1378 GstVideoAggregatorPad *pad = l->data;
1379 GstVideoFrame *prepared_frame =
1380 gst_video_aggregator_pad_get_prepared_frame (pad);
1381
1382 if (prepared_frame)
1383 n_pads++;
1384 }
1385
1386 /* If no prepared frame, we should draw background unconditionally in order
1387 * to clear output buffer */
1388 if (n_pads == 0)
1389 draw_background = TRUE;
1390
1391 pads_info = g_newa (struct CompositePadInfo, n_pads);
1392 n_pads = 0;
1393
1394 for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
1395 GstVideoAggregatorPad *pad = l->data;
1396 GstCompositorPad *compo_pad = GST_COMPOSITOR_PAD (pad);
1397 GstVideoFrame *prepared_frame =
1398 gst_video_aggregator_pad_get_prepared_frame (pad);
1399 GstCompositorBlendMode blend_mode = COMPOSITOR_BLEND_MODE_OVER;
1400
1401 switch (compo_pad->op) {
1402 case COMPOSITOR_OPERATOR_SOURCE:
1403 blend_mode = COMPOSITOR_BLEND_MODE_SOURCE;
1404 break;
1405 case COMPOSITOR_OPERATOR_OVER:
1406 blend_mode = COMPOSITOR_BLEND_MODE_OVER;
1407 break;
1408 case COMPOSITOR_OPERATOR_ADD:
1409 blend_mode = COMPOSITOR_BLEND_MODE_ADD;
1410 break;
1411 default:
1412 g_assert_not_reached ();
1413 break;
1414 }
1415
1416 if (prepared_frame != NULL) {
1417 /* If this is the first pad we're drawing, and we didn't draw the
1418 * background, and @prepared_frame has the same format, height, and width
1419 * as @outframe, then we can just copy it as-is. Subsequent pads (if any)
1420 * will be composited on top of it. */
1421 if (!drawn_a_pad && !draw_background &&
1422 frames_can_copy (prepared_frame, outframe)) {
1423 gst_video_frame_copy (outframe, prepared_frame);
1424 } else {
1425 pads_info[n_pads].pad = compo_pad;
1426 pads_info[n_pads].prepared_frame = prepared_frame;
1427 pads_info[n_pads].blend_mode = blend_mode;
1428 n_pads++;
1429 }
1430 drawn_a_pad = TRUE;
1431 }
1432 }
1433
1434 {
1435 guint n_threads, lines_per_thread;
1436 guint out_height;
1437 struct CompositeTask *tasks;
1438 struct CompositeTask **tasks_p;
1439
1440 n_threads = compositor->blend_runner->n_threads;
1441
1442 tasks = g_newa (struct CompositeTask, n_threads);
1443 tasks_p = g_newa (struct CompositeTask *, n_threads);
1444
1445 out_height = GST_VIDEO_FRAME_HEIGHT (outframe);
1446 lines_per_thread = (out_height + n_threads - 1) / n_threads;
1447
1448 for (i = 0; i < n_threads; i++) {
1449 tasks[i].compositor = compositor;
1450 tasks[i].n_pads = n_pads;
1451 tasks[i].pads_info = pads_info;
1452 tasks[i].out_frame = outframe;
1453 tasks[i].draw_background = draw_background;
1454 /* This is a dumb split of the work by number of output lines.
1455 * If there is a section of the output that reads from a lot of source
1456 * pads, then that thread will consume more time. Maybe tracking and
1457 * splitting on the source fill rate would produce better results. */
1458 tasks[i].dst_line_start = i * lines_per_thread;
1459 tasks[i].dst_line_end = MIN ((i + 1) * lines_per_thread, out_height);
1460
1461 tasks_p[i] = &tasks[i];
1462 }
1463
1464 gst_parallelized_task_runner_run (compositor->blend_runner,
1465 (GstParallelizedTaskFunc) blend_pads, (gpointer *) tasks_p);
1466 }
1467
1468 GST_OBJECT_UNLOCK (vagg);
1469
1470 gst_video_frame_unmap (outframe);
1471
1472 return GST_FLOW_OK;
1473 }
1474
1475 static GstPad *
gst_compositor_request_new_pad(GstElement * element,GstPadTemplate * templ,const gchar * req_name,const GstCaps * caps)1476 gst_compositor_request_new_pad (GstElement * element, GstPadTemplate * templ,
1477 const gchar * req_name, const GstCaps * caps)
1478 {
1479 GstPad *newpad;
1480
1481 newpad = (GstPad *)
1482 GST_ELEMENT_CLASS (parent_class)->request_new_pad (element,
1483 templ, req_name, caps);
1484
1485 if (newpad == NULL)
1486 goto could_not_create;
1487
1488 gst_child_proxy_child_added (GST_CHILD_PROXY (element), G_OBJECT (newpad),
1489 GST_OBJECT_NAME (newpad));
1490
1491 return newpad;
1492
1493 could_not_create:
1494 {
1495 GST_DEBUG_OBJECT (element, "could not create/add pad");
1496 return NULL;
1497 }
1498 }
1499
1500 static void
gst_compositor_release_pad(GstElement * element,GstPad * pad)1501 gst_compositor_release_pad (GstElement * element, GstPad * pad)
1502 {
1503 GstCompositor *compositor;
1504
1505 compositor = GST_COMPOSITOR (element);
1506
1507 GST_DEBUG_OBJECT (compositor, "release pad %s:%s", GST_DEBUG_PAD_NAME (pad));
1508
1509 gst_child_proxy_child_removed (GST_CHILD_PROXY (compositor), G_OBJECT (pad),
1510 GST_OBJECT_NAME (pad));
1511
1512 GST_ELEMENT_CLASS (parent_class)->release_pad (element, pad);
1513 }
1514
1515 static gboolean
_sink_query(GstAggregator * agg,GstAggregatorPad * bpad,GstQuery * query)1516 _sink_query (GstAggregator * agg, GstAggregatorPad * bpad, GstQuery * query)
1517 {
1518 switch (GST_QUERY_TYPE (query)) {
1519 case GST_QUERY_ALLOCATION:{
1520 GstCaps *caps;
1521 GstVideoInfo info;
1522 GstBufferPool *pool;
1523 guint size;
1524 GstStructure *structure;
1525
1526 gst_query_parse_allocation (query, &caps, NULL);
1527
1528 if (caps == NULL)
1529 return FALSE;
1530
1531 if (!gst_video_info_from_caps (&info, caps))
1532 return FALSE;
1533
1534 size = GST_VIDEO_INFO_SIZE (&info);
1535
1536 pool = gst_video_buffer_pool_new ();
1537
1538 structure = gst_buffer_pool_get_config (pool);
1539 gst_buffer_pool_config_set_params (structure, caps, size, 0, 0);
1540
1541 if (!gst_buffer_pool_set_config (pool, structure)) {
1542 gst_object_unref (pool);
1543 return FALSE;
1544 }
1545
1546 gst_query_add_allocation_pool (query, pool, size, 0, 0);
1547 gst_object_unref (pool);
1548 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
1549
1550 return TRUE;
1551 }
1552 default:
1553 return GST_AGGREGATOR_CLASS (parent_class)->sink_query (agg, bpad, query);
1554 }
1555 }
1556
1557 static void
gst_compositor_finalize(GObject * object)1558 gst_compositor_finalize (GObject * object)
1559 {
1560 GstCompositor *compositor = GST_COMPOSITOR (object);
1561
1562 if (compositor->blend_runner)
1563 gst_parallelized_task_runner_free (compositor->blend_runner);
1564 compositor->blend_runner = NULL;
1565
1566 G_OBJECT_CLASS (parent_class)->finalize (object);
1567 }
1568
1569 /* GObject boilerplate */
1570 static void
gst_compositor_class_init(GstCompositorClass * klass)1571 gst_compositor_class_init (GstCompositorClass * klass)
1572 {
1573 GObjectClass *gobject_class = (GObjectClass *) klass;
1574 GstElementClass *gstelement_class = (GstElementClass *) klass;
1575 GstVideoAggregatorClass *videoaggregator_class =
1576 (GstVideoAggregatorClass *) klass;
1577 GstAggregatorClass *agg_class = (GstAggregatorClass *) klass;
1578
1579 gobject_class->get_property = gst_compositor_get_property;
1580 gobject_class->set_property = gst_compositor_set_property;
1581 gobject_class->finalize = gst_compositor_finalize;
1582
1583 gstelement_class->request_new_pad =
1584 GST_DEBUG_FUNCPTR (gst_compositor_request_new_pad);
1585 gstelement_class->release_pad =
1586 GST_DEBUG_FUNCPTR (gst_compositor_release_pad);
1587 agg_class->sink_query = _sink_query;
1588 agg_class->fixate_src_caps = _fixate_caps;
1589 agg_class->negotiated_src_caps = _negotiated_caps;
1590 videoaggregator_class->aggregate_frames = gst_compositor_aggregate_frames;
1591
1592 g_object_class_install_property (gobject_class, PROP_BACKGROUND,
1593 g_param_spec_enum ("background", "Background", "Background type",
1594 GST_TYPE_COMPOSITOR_BACKGROUND,
1595 DEFAULT_BACKGROUND, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1596
1597 /**
1598 * compositor:zero-size-is-unscaled:
1599 *
1600 * Whether a pad with height or width 0 should be left unscaled
1601 * in that dimension, or simply not composited in. Setting it to
1602 * %FALSE might be useful when animating those properties.
1603 *
1604 * Since: 1.20
1605 */
1606 g_object_class_install_property (gobject_class, PROP_ZERO_SIZE_IS_UNSCALED,
1607 g_param_spec_boolean ("zero-size-is-unscaled", "Zero size is unscaled",
1608 "If TRUE, then input video is unscaled in that dimension "
1609 "if width or height is 0 (for backwards compatibility)",
1610 DEFAULT_ZERO_SIZE_IS_UNSCALED,
1611 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1612
1613 /**
1614 * compositor:max-threads:
1615 *
1616 * Maximum number of blending/rendering worker threads to spawn (0 = auto)
1617 *
1618 * Since: 1.20
1619 */
1620 g_object_class_install_property (gobject_class, PROP_MAX_THREADS,
1621 g_param_spec_uint ("max-threads", "Max Threads",
1622 "Maximum number of blending/rendering worker threads to spawn "
1623 "(0 = auto)", 0, G_MAXINT, DEFAULT_MAX_THREADS,
1624 GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE |
1625 G_PARAM_STATIC_STRINGS));
1626
1627 gst_element_class_add_static_pad_template_with_gtype (gstelement_class,
1628 &src_factory, GST_TYPE_AGGREGATOR_PAD);
1629 gst_element_class_add_static_pad_template_with_gtype (gstelement_class,
1630 &sink_factory, GST_TYPE_COMPOSITOR_PAD);
1631
1632 gst_element_class_set_static_metadata (gstelement_class, "Compositor",
1633 "Filter/Editor/Video/Compositor",
1634 "Composite multiple video streams", "Wim Taymans <wim@fluendo.com>, "
1635 "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
1636
1637 /**
1638 * compositor:ignore-inactive-pads:
1639 *
1640 * Don't wait for inactive pads when live. An inactive pad
1641 * is a pad that hasn't yet received a buffer, but that has
1642 * been waited on at least once.
1643 *
1644 * The purpose of this property is to avoid aggregating on
1645 * timeout when new pads are requested in advance of receiving
1646 * data flow, for example the user may decide to connect it later,
1647 * but wants to configure it already.
1648 *
1649 * Since: 1.20
1650 */
1651 g_object_class_install_property (gobject_class,
1652 PROP_IGNORE_INACTIVE_PADS, g_param_spec_boolean ("ignore-inactive-pads",
1653 "Ignore inactive pads",
1654 "Avoid timing out waiting for inactive pads", FALSE,
1655 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1656
1657 gst_type_mark_as_plugin_api (GST_TYPE_COMPOSITOR_PAD, 0);
1658 gst_type_mark_as_plugin_api (GST_TYPE_COMPOSITOR_OPERATOR, 0);
1659 gst_type_mark_as_plugin_api (GST_TYPE_COMPOSITOR_BACKGROUND, 0);
1660 }
1661
1662 static void
gst_compositor_init(GstCompositor * self)1663 gst_compositor_init (GstCompositor * self)
1664 {
1665 /* initialize variables */
1666 self->background = DEFAULT_BACKGROUND;
1667 self->zero_size_is_unscaled = DEFAULT_ZERO_SIZE_IS_UNSCALED;
1668 self->max_threads = DEFAULT_MAX_THREADS;
1669 }
1670
1671 /* GstChildProxy implementation */
1672 static GObject *
gst_compositor_child_proxy_get_child_by_index(GstChildProxy * child_proxy,guint index)1673 gst_compositor_child_proxy_get_child_by_index (GstChildProxy * child_proxy,
1674 guint index)
1675 {
1676 GstCompositor *compositor = GST_COMPOSITOR (child_proxy);
1677 GObject *obj = NULL;
1678
1679 GST_OBJECT_LOCK (compositor);
1680 obj = g_list_nth_data (GST_ELEMENT_CAST (compositor)->sinkpads, index);
1681 if (obj)
1682 gst_object_ref (obj);
1683 GST_OBJECT_UNLOCK (compositor);
1684
1685 return obj;
1686 }
1687
1688 static guint
gst_compositor_child_proxy_get_children_count(GstChildProxy * child_proxy)1689 gst_compositor_child_proxy_get_children_count (GstChildProxy * child_proxy)
1690 {
1691 guint count = 0;
1692 GstCompositor *compositor = GST_COMPOSITOR (child_proxy);
1693
1694 GST_OBJECT_LOCK (compositor);
1695 count = GST_ELEMENT_CAST (compositor)->numsinkpads;
1696 GST_OBJECT_UNLOCK (compositor);
1697 GST_INFO_OBJECT (compositor, "Children Count: %d", count);
1698
1699 return count;
1700 }
1701
1702 static void
gst_compositor_child_proxy_init(gpointer g_iface,gpointer iface_data)1703 gst_compositor_child_proxy_init (gpointer g_iface, gpointer iface_data)
1704 {
1705 GstChildProxyInterface *iface = g_iface;
1706
1707 iface->get_child_by_index = gst_compositor_child_proxy_get_child_by_index;
1708 iface->get_children_count = gst_compositor_child_proxy_get_children_count;
1709 }
1710
1711 /* Element registration */
1712 static gboolean
plugin_init(GstPlugin * plugin)1713 plugin_init (GstPlugin * plugin)
1714 {
1715 GST_DEBUG_CATEGORY_INIT (gst_compositor_debug, "compositor", 0, "compositor");
1716
1717 gst_compositor_init_blend ();
1718
1719 return GST_ELEMENT_REGISTER (compositor, plugin);
1720 }
1721
1722 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1723 GST_VERSION_MINOR,
1724 compositor,
1725 "Compositor", plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME,
1726 GST_PACKAGE_ORIGIN)
1727