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>
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.
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.
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.
24 * SECTION:element-compositor
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.
32 * Compositor will do colorspace conversion.
34 * Individual parameters for each input stream can be configured on the
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)
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 \
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.
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 \
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).
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 \
74 * video/x-raw,format=I420, framerate=\(fraction\)5/1, width=320, height=240 ! comp.
75 * ]| A pipeline to test I420
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)
95 #include "compositor.h"
98 #define orc_memset memset
100 #include <orc/orcfunctions.h>
103 GST_DEBUG_CATEGORY_STATIC (gst_compositor_debug);
104 #define GST_CAT_DEFAULT gst_compositor_debug
106 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
107 #define FORMATS " { AYUV, VUYA, BGRA, ARGB, RGBA, ABGR, Y444, Y42B, YUY2, UYVY, "\
108 " YVYU, I422_12LE, I422_12BE, I422_10LE, I422_10BE, "\
109 " I420_12LE, I420_12BE, I420_10LE, I420_10BE, " \
110 " I420, YV12, NV12, NV21, Y41B, RGB, BGR, xRGB, xBGR, "\
113 #define FORMATS " { AYUV, VUYA, BGRA, ARGB, RGBA, ABGR, Y444, Y42B, YUY2, UYVY, "\
114 " YVYU, I422_12BE, I422_12LE, I422_10BE, I422_10LE, "\
115 " I420_12BE, I420_12LE, I420_10BE, I420_10LE, "\
116 " I420, YV12, NV12, NV21, Y41B, RGB, BGR, xRGB, xBGR, "\
120 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
123 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (FORMATS))
126 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%u",
129 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL))
132 static void gst_compositor_child_proxy_init (gpointer g_iface,
133 gpointer iface_data);
135 #define GST_TYPE_COMPOSITOR_OPERATOR (gst_compositor_operator_get_type())
137 gst_compositor_operator_get_type (void)
139 static GType compositor_operator_type = 0;
141 static const GEnumValue compositor_operator[] = {
142 {COMPOSITOR_OPERATOR_SOURCE, "Source", "source"},
143 {COMPOSITOR_OPERATOR_OVER, "Over", "over"},
144 {COMPOSITOR_OPERATOR_ADD, "Add", "add"},
148 if (!compositor_operator_type) {
149 compositor_operator_type =
150 g_enum_register_static ("GstCompositorOperator", compositor_operator);
152 return compositor_operator_type;
155 #define GST_TYPE_COMPOSITOR_BACKGROUND (gst_compositor_background_get_type())
157 gst_compositor_background_get_type (void)
159 static GType compositor_background_type = 0;
161 static const GEnumValue compositor_background[] = {
162 {COMPOSITOR_BACKGROUND_CHECKER, "Checker pattern", "checker"},
163 {COMPOSITOR_BACKGROUND_BLACK, "Black", "black"},
164 {COMPOSITOR_BACKGROUND_WHITE, "White", "white"},
165 {COMPOSITOR_BACKGROUND_TRANSPARENT,
166 "Transparent Background to enable further compositing", "transparent"},
170 if (!compositor_background_type) {
171 compositor_background_type =
172 g_enum_register_static ("GstCompositorBackground",
173 compositor_background);
175 return compositor_background_type;
178 #define GST_TYPE_COMPOSITOR_SIZING_POLICY (gst_compositor_sizing_policy_get_type())
180 gst_compositor_sizing_policy_get_type (void)
182 static GType sizing_policy_type = 0;
184 static const GEnumValue sizing_polices[] = {
185 {COMPOSITOR_SIZING_POLICY_NONE,
186 "None: Image is scaled to fill configured destination rectangle without "
187 "padding or keeping the aspect ratio", "none"},
188 {COMPOSITOR_SIZING_POLICY_KEEP_ASPECT_RATIO,
189 "Keep Aspect Ratio: Image is scaled to fit destination rectangle "
190 "specified by GstCompositorPad:{xpos, ypos, width, height} "
191 "with preserved aspect ratio. Resulting image will be centered in "
192 "the destination rectangle with padding if necessary",
193 "keep-aspect-ratio"},
197 if (!sizing_policy_type) {
199 g_enum_register_static ("GstCompositorSizingPolicy", sizing_polices);
201 return sizing_policy_type;
204 #define DEFAULT_PAD_XPOS 0
205 #define DEFAULT_PAD_YPOS 0
206 #define DEFAULT_PAD_WIDTH -1
207 #define DEFAULT_PAD_HEIGHT -1
208 #define DEFAULT_PAD_ALPHA 1.0
209 #define DEFAULT_PAD_OPERATOR COMPOSITOR_OPERATOR_OVER
210 #define DEFAULT_PAD_SIZING_POLICY COMPOSITOR_SIZING_POLICY_NONE
221 PROP_PAD_SIZING_POLICY,
224 G_DEFINE_TYPE (GstCompositorPad, gst_compositor_pad,
225 GST_TYPE_VIDEO_AGGREGATOR_PARALLEL_CONVERT_PAD);
228 gst_compositor_pad_get_property (GObject * object, guint prop_id,
229 GValue * value, GParamSpec * pspec)
231 GstCompositorPad *pad = GST_COMPOSITOR_PAD (object);
235 g_value_set_int (value, pad->xpos);
238 g_value_set_int (value, pad->ypos);
241 g_value_set_int (value, pad->width);
243 case PROP_PAD_HEIGHT:
244 g_value_set_int (value, pad->height);
247 g_value_set_double (value, pad->alpha);
249 case PROP_PAD_OPERATOR:
250 g_value_set_enum (value, pad->op);
252 case PROP_PAD_SIZING_POLICY:
253 g_value_set_enum (value, pad->sizing_policy);
256 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
262 gst_compositor_pad_set_property (GObject * object, guint prop_id,
263 const GValue * value, GParamSpec * pspec)
265 GstCompositorPad *pad = GST_COMPOSITOR_PAD (object);
269 pad->xpos = g_value_get_int (value);
272 pad->ypos = g_value_get_int (value);
275 pad->width = g_value_get_int (value);
276 gst_video_aggregator_convert_pad_update_conversion_info
277 (GST_VIDEO_AGGREGATOR_CONVERT_PAD (pad));
279 case PROP_PAD_HEIGHT:
280 pad->height = g_value_get_int (value);
281 gst_video_aggregator_convert_pad_update_conversion_info
282 (GST_VIDEO_AGGREGATOR_CONVERT_PAD (pad));
285 pad->alpha = g_value_get_double (value);
287 case PROP_PAD_OPERATOR:
288 pad->op = g_value_get_enum (value);
289 gst_video_aggregator_pad_set_needs_alpha (GST_VIDEO_AGGREGATOR_PAD (pad),
290 pad->op == COMPOSITOR_OPERATOR_ADD);
292 case PROP_PAD_SIZING_POLICY:
293 pad->sizing_policy = g_value_get_enum (value);
294 gst_video_aggregator_convert_pad_update_conversion_info
295 (GST_VIDEO_AGGREGATOR_CONVERT_PAD (pad));
298 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
304 _mixer_pad_get_output_size (GstCompositor * comp, GstCompositorPad * comp_pad,
305 gint out_par_n, gint out_par_d, gint * width, gint * height,
306 gint * x_offset, gint * y_offset)
308 GstVideoAggregatorPad *vagg_pad = GST_VIDEO_AGGREGATOR_PAD (comp_pad);
309 gint pad_width, pad_height;
317 /* FIXME: Anything better we can do here? */
318 if (!vagg_pad->info.finfo
319 || vagg_pad->info.finfo->format == GST_VIDEO_FORMAT_UNKNOWN) {
320 GST_DEBUG_OBJECT (comp_pad, "Have no caps yet");
324 if (comp->zero_size_is_unscaled) {
327 0 ? GST_VIDEO_INFO_WIDTH (&vagg_pad->info) : comp_pad->width;
330 0 ? GST_VIDEO_INFO_HEIGHT (&vagg_pad->info) : comp_pad->height;
334 0 ? GST_VIDEO_INFO_WIDTH (&vagg_pad->info) : comp_pad->width;
337 0 ? GST_VIDEO_INFO_HEIGHT (&vagg_pad->info) : comp_pad->height;
340 if (pad_width == 0 || pad_height == 0)
343 if (!gst_video_calculate_display_ratio (&dar_n, &dar_d, pad_width, pad_height,
344 GST_VIDEO_INFO_PAR_N (&vagg_pad->info),
345 GST_VIDEO_INFO_PAR_D (&vagg_pad->info), out_par_n, out_par_d)) {
346 GST_WARNING_OBJECT (comp_pad, "Cannot calculate display aspect ratio");
350 GST_LOG_OBJECT (comp_pad, "scaling %ux%u by %u/%u (%u/%u / %u/%u)", pad_width,
351 pad_height, dar_n, dar_d, GST_VIDEO_INFO_PAR_N (&vagg_pad->info),
352 GST_VIDEO_INFO_PAR_D (&vagg_pad->info), out_par_n, out_par_d);
354 switch (comp_pad->sizing_policy) {
355 case COMPOSITOR_SIZING_POLICY_NONE:
356 /* Pick either height or width, whichever is an integer multiple of the
357 * display aspect ratio. However, prefer preserving the height to account
358 * for interlaced video. */
359 if (pad_height % dar_n == 0) {
360 pad_width = gst_util_uint64_scale_int (pad_height, dar_n, dar_d);
361 } else if (pad_width % dar_d == 0) {
362 pad_height = gst_util_uint64_scale_int (pad_width, dar_d, dar_n);
364 pad_width = gst_util_uint64_scale_int (pad_height, dar_n, dar_d);
367 case COMPOSITOR_SIZING_POLICY_KEEP_ASPECT_RATIO:
369 gint from_dar_n, from_dar_d, to_dar_n, to_dar_d, num, den;
371 /* Calculate DAR again with actual video size */
372 if (!gst_util_fraction_multiply (GST_VIDEO_INFO_WIDTH (&vagg_pad->info),
373 GST_VIDEO_INFO_HEIGHT (&vagg_pad->info),
374 GST_VIDEO_INFO_PAR_N (&vagg_pad->info),
375 GST_VIDEO_INFO_PAR_D (&vagg_pad->info), &from_dar_n,
377 from_dar_n = from_dar_d = -1;
380 if (!gst_util_fraction_multiply (pad_width, pad_height,
381 out_par_n, out_par_d, &to_dar_n, &to_dar_d)) {
382 to_dar_n = to_dar_d = -1;
385 if (from_dar_n != to_dar_n || from_dar_d != to_dar_d) {
386 /* Calculate new output resolution */
387 if (from_dar_n != -1 && from_dar_d != -1
388 && gst_util_fraction_multiply (from_dar_n, from_dar_d,
389 out_par_d, out_par_n, &num, &den)) {
390 GstVideoRectangle src_rect, dst_rect, rst_rect;
392 src_rect.h = gst_util_uint64_scale_int (pad_width, den, num);
393 if (src_rect.h == 0) {
399 src_rect.x = src_rect.y = 0;
400 src_rect.w = pad_width;
402 dst_rect.x = dst_rect.y = 0;
403 dst_rect.w = pad_width;
404 dst_rect.h = pad_height;
406 /* Scale rect to be centered in destination rect */
407 gst_video_center_rect (&src_rect, &dst_rect, &rst_rect, TRUE);
409 GST_LOG_OBJECT (comp_pad,
410 "Re-calculated size %dx%d -> %dx%d (x-offset %d, y-offset %d)",
411 pad_width, pad_height, rst_rect.w, rst_rect.h, rst_rect.x,
414 *x_offset = rst_rect.x;
415 *y_offset = rst_rect.y;
416 pad_width = rst_rect.w;
417 pad_height = rst_rect.h;
419 GST_WARNING_OBJECT (comp_pad, "Failed to calculate output size");
432 *height = pad_height;
436 is_point_contained (const GstVideoRectangle rect, const gint px, const gint py)
438 if ((px >= rect.x) && (px <= rect.x + rect.w) &&
439 (py >= rect.y) && (py <= rect.y + rect.h))
444 /* Test whether rectangle2 contains rectangle 1 (geometrically) */
446 is_rectangle_contained (const GstVideoRectangle rect1,
447 const GstVideoRectangle rect2)
449 if ((rect2.x <= rect1.x) && (rect2.y <= rect1.y) &&
450 ((rect2.x + rect2.w) >= (rect1.x + rect1.w)) &&
451 ((rect2.y + rect2.h) >= (rect1.y + rect1.h)))
456 static GstVideoRectangle
457 clamp_rectangle (gint x, gint y, gint w, gint h, gint outer_width,
462 GstVideoRectangle clamped;
464 /* Clamp the x/y coordinates of this frame to the output boundaries to cover
465 * the case where (say, with negative xpos/ypos or w/h greater than the output
466 * size) the non-obscured portion of the frame could be outside the bounds of
467 * the video itself and hence not visible at all */
468 clamped.x = CLAMP (x, 0, outer_width);
469 clamped.y = CLAMP (y, 0, outer_height);
470 clamped.w = CLAMP (x2, 0, outer_width) - clamped.x;
471 clamped.h = CLAMP (y2, 0, outer_height) - clamped.y;
476 /* Call this with the lock taken */
478 _pad_obscures_rectangle (GstVideoAggregator * vagg, GstVideoAggregatorPad * pad,
479 const GstVideoRectangle rect)
481 GstVideoRectangle pad_rect;
482 GstCompositorPad *cpad = GST_COMPOSITOR_PAD (pad);
483 GstStructure *converter_config = NULL;
484 gboolean fill_border = TRUE;
485 guint32 border_argb = 0xff000000;
486 gint x_offset, y_offset;
488 /* No buffer to obscure the rectangle with */
489 if (!gst_video_aggregator_pad_has_current_buffer (pad))
492 /* Can't obscure if we introduce alpha or if the format has an alpha
493 * component as we'd have to inspect every pixel to know if the frame is
494 * opaque, so assume it doesn't obscure
496 if (cpad->alpha != 1.0 || GST_VIDEO_INFO_HAS_ALPHA (&pad->info))
499 /* If a converter-config is set and it is either configured to not fill any
500 * borders, or configured to use a non-opaque color, then we have to handle
501 * the pad as potentially containing transparency */
502 g_object_get (pad, "converter-config", &converter_config, NULL);
503 if (converter_config) {
504 gst_structure_get (converter_config, GST_VIDEO_CONVERTER_OPT_BORDER_ARGB,
505 G_TYPE_UINT, &border_argb, NULL);
506 gst_structure_get (converter_config, GST_VIDEO_CONVERTER_OPT_FILL_BORDER,
507 G_TYPE_BOOLEAN, &fill_border, NULL);
509 gst_clear_structure (&converter_config);
510 if (!fill_border || (border_argb & 0xff000000) != 0xff000000)
513 pad_rect.x = cpad->xpos;
514 pad_rect.y = cpad->ypos;
515 /* Handle pixel and display aspect ratios to find the actual size */
516 _mixer_pad_get_output_size (GST_COMPOSITOR (vagg), cpad,
517 GST_VIDEO_INFO_PAR_N (&vagg->info), GST_VIDEO_INFO_PAR_D (&vagg->info),
518 &(pad_rect.w), &(pad_rect.h), &x_offset, &y_offset);
519 pad_rect.x += x_offset;
520 pad_rect.y += y_offset;
522 if (!is_rectangle_contained (rect, pad_rect))
525 GST_DEBUG_OBJECT (pad, "Pad %s %ix%i@(%i,%i) obscures rect %ix%i@(%i,%i)",
526 GST_PAD_NAME (pad), pad_rect.w, pad_rect.h, pad_rect.x, pad_rect.y,
527 rect.w, rect.h, rect.x, rect.y);
533 gst_compositor_pad_prepare_frame_start (GstVideoAggregatorPad * pad,
534 GstVideoAggregator * vagg, GstBuffer * buffer,
535 GstVideoFrame * prepared_frame)
537 GstCompositorPad *cpad = GST_COMPOSITOR_PAD (pad);
539 gboolean frame_obscured = FALSE;
541 /* The rectangle representing this frame, clamped to the video's boundaries.
542 * Due to the clamping, this is different from the frame width/height above. */
543 GstVideoRectangle frame_rect;
545 /* There's three types of width/height here:
546 * 1. GST_VIDEO_FRAME_WIDTH/HEIGHT:
547 * The frame width/height (same as pad->info.height/width;
548 * see gst_video_frame_map())
549 * 2. cpad->width/height:
550 * The optional pad property for scaling the frame (if zero, the video is
552 * 3. conversion_info.width/height:
553 * Equal to cpad->width/height if it's set, otherwise it's the pad
554 * width/height. See ->set_info()
557 _mixer_pad_get_output_size (GST_COMPOSITOR (vagg), cpad,
558 GST_VIDEO_INFO_PAR_N (&vagg->info), GST_VIDEO_INFO_PAR_D (&vagg->info),
559 &width, &height, &cpad->x_offset, &cpad->y_offset);
561 if (cpad->alpha == 0.0) {
562 GST_DEBUG_OBJECT (pad, "Pad has alpha 0.0, not converting frame");
566 if (gst_aggregator_pad_is_inactive (GST_AGGREGATOR_PAD (pad)))
569 frame_rect = clamp_rectangle (cpad->xpos + cpad->x_offset,
570 cpad->ypos + cpad->y_offset, width, height,
571 GST_VIDEO_INFO_WIDTH (&vagg->info), GST_VIDEO_INFO_HEIGHT (&vagg->info));
573 if (frame_rect.w == 0 || frame_rect.h == 0) {
574 GST_DEBUG_OBJECT (pad, "Resulting frame is zero-width or zero-height "
575 "(w: %i, h: %i), skipping", frame_rect.w, frame_rect.h);
579 GST_OBJECT_LOCK (vagg);
580 /* Check if this frame is obscured by a higher-zorder frame
581 * TODO: Also skip a frame if it's obscured by a combination of
582 * higher-zorder frames */
583 l = g_list_find (GST_ELEMENT (vagg)->sinkpads, pad);
584 /* The pad might've just been removed */
587 for (; l; l = l->next) {
588 GstBuffer *pad_buffer;
591 gst_video_aggregator_pad_get_current_buffer (GST_VIDEO_AGGREGATOR_PAD
594 if (pad_buffer == NULL)
597 if (gst_buffer_get_size (pad_buffer) == 0 &&
598 GST_BUFFER_FLAG_IS_SET (pad_buffer, GST_BUFFER_FLAG_GAP)) {
602 if (_pad_obscures_rectangle (vagg, l->data, frame_rect)) {
603 frame_obscured = TRUE;
607 GST_OBJECT_UNLOCK (vagg);
612 GST_VIDEO_AGGREGATOR_PAD_CLASS
613 (gst_compositor_pad_parent_class)->prepare_frame_start (pad, vagg, buffer,
618 gst_compositor_pad_create_conversion_info (GstVideoAggregatorConvertPad * pad,
619 GstVideoAggregator * vagg, GstVideoInfo * conversion_info)
621 GstCompositorPad *cpad = GST_COMPOSITOR_PAD (pad);
623 gint x_offset, y_offset;
625 GST_VIDEO_AGGREGATOR_CONVERT_PAD_CLASS
626 (gst_compositor_pad_parent_class)->create_conversion_info (pad, vagg,
628 if (!conversion_info->finfo)
631 _mixer_pad_get_output_size (GST_COMPOSITOR (vagg), cpad,
632 GST_VIDEO_INFO_PAR_N (&vagg->info), GST_VIDEO_INFO_PAR_D (&vagg->info),
633 &width, &height, &x_offset, &y_offset);
635 /* The only thing that can change here is the width
636 * and height, otherwise set_info would've been called */
637 if (GST_VIDEO_INFO_WIDTH (conversion_info) != width ||
638 GST_VIDEO_INFO_HEIGHT (conversion_info) != height) {
639 GstVideoInfo tmp_info;
641 /* Initialize with the wanted video format and our original width and
642 * height as we don't want to rescale. Then copy over the wanted
643 * colorimetry, and chroma-site and our current pixel-aspect-ratio
644 * and other relevant fields.
646 gst_video_info_set_format (&tmp_info,
647 GST_VIDEO_INFO_FORMAT (conversion_info), width, height);
648 tmp_info.chroma_site = conversion_info->chroma_site;
649 tmp_info.colorimetry = conversion_info->colorimetry;
650 tmp_info.par_n = conversion_info->par_n;
651 tmp_info.par_d = conversion_info->par_d;
652 tmp_info.fps_n = conversion_info->fps_n;
653 tmp_info.fps_d = conversion_info->fps_d;
654 tmp_info.flags = conversion_info->flags;
655 tmp_info.interlace_mode = conversion_info->interlace_mode;
657 *conversion_info = tmp_info;
662 gst_compositor_pad_class_init (GstCompositorPadClass * klass)
664 GObjectClass *gobject_class = (GObjectClass *) klass;
665 GstVideoAggregatorPadClass *vaggpadclass =
666 (GstVideoAggregatorPadClass *) klass;
667 GstVideoAggregatorConvertPadClass *vaggcpadclass =
668 (GstVideoAggregatorConvertPadClass *) klass;
670 gobject_class->set_property = gst_compositor_pad_set_property;
671 gobject_class->get_property = gst_compositor_pad_get_property;
673 g_object_class_install_property (gobject_class, PROP_PAD_XPOS,
674 g_param_spec_int ("xpos", "X Position", "X Position of the picture",
675 G_MININT, G_MAXINT, DEFAULT_PAD_XPOS,
676 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
677 g_object_class_install_property (gobject_class, PROP_PAD_YPOS,
678 g_param_spec_int ("ypos", "Y Position", "Y Position of the picture",
679 G_MININT, G_MAXINT, DEFAULT_PAD_YPOS,
680 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
681 g_object_class_install_property (gobject_class, PROP_PAD_WIDTH,
682 g_param_spec_int ("width", "Width", "Width of the picture",
683 G_MININT, G_MAXINT, DEFAULT_PAD_WIDTH,
684 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
685 g_object_class_install_property (gobject_class, PROP_PAD_HEIGHT,
686 g_param_spec_int ("height", "Height", "Height of the picture",
687 G_MININT, G_MAXINT, DEFAULT_PAD_HEIGHT,
688 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
689 g_object_class_install_property (gobject_class, PROP_PAD_ALPHA,
690 g_param_spec_double ("alpha", "Alpha", "Alpha of the picture", 0.0, 1.0,
692 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
693 g_object_class_install_property (gobject_class, PROP_PAD_OPERATOR,
694 g_param_spec_enum ("operator", "Operator",
695 "Blending operator to use for blending this pad over the previous ones",
696 GST_TYPE_COMPOSITOR_OPERATOR, DEFAULT_PAD_OPERATOR,
697 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
700 * GstCompositorPad:sizing-policy:
702 * Specifies sizing policy to use. Depending on selected sizing policy,
703 * scaled image might not fully cover the configured target rectangle area
704 * (e.g., "keep-aspect-ratio"). In that case, any uncovered area will be
705 * filled with background unless the uncovered area is drawn by other image.
709 g_object_class_install_property (gobject_class, PROP_PAD_SIZING_POLICY,
710 g_param_spec_enum ("sizing-policy", "Sizing policy",
711 "Sizing policy to use for image scaling",
712 GST_TYPE_COMPOSITOR_SIZING_POLICY, DEFAULT_PAD_SIZING_POLICY,
713 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
715 vaggpadclass->prepare_frame_start =
716 GST_DEBUG_FUNCPTR (gst_compositor_pad_prepare_frame_start);
718 vaggcpadclass->create_conversion_info =
719 GST_DEBUG_FUNCPTR (gst_compositor_pad_create_conversion_info);
721 gst_type_mark_as_plugin_api (GST_TYPE_COMPOSITOR_SIZING_POLICY, 0);
725 gst_compositor_pad_init (GstCompositorPad * compo_pad)
727 compo_pad->xpos = DEFAULT_PAD_XPOS;
728 compo_pad->ypos = DEFAULT_PAD_YPOS;
729 compo_pad->alpha = DEFAULT_PAD_ALPHA;
730 compo_pad->op = DEFAULT_PAD_OPERATOR;
731 compo_pad->width = DEFAULT_PAD_WIDTH;
732 compo_pad->height = DEFAULT_PAD_HEIGHT;
733 compo_pad->sizing_policy = DEFAULT_PAD_SIZING_POLICY;
738 #define DEFAULT_BACKGROUND COMPOSITOR_BACKGROUND_CHECKER
739 #define DEFAULT_ZERO_SIZE_IS_UNSCALED TRUE
740 #define DEFAULT_MAX_THREADS 0
746 PROP_ZERO_SIZE_IS_UNSCALED,
748 PROP_IGNORE_INACTIVE_PADS,
752 gst_compositor_get_property (GObject * object,
753 guint prop_id, GValue * value, GParamSpec * pspec)
755 GstCompositor *self = GST_COMPOSITOR (object);
758 case PROP_BACKGROUND:
759 g_value_set_enum (value, self->background);
761 case PROP_ZERO_SIZE_IS_UNSCALED:
762 g_value_set_boolean (value, self->zero_size_is_unscaled);
764 case PROP_MAX_THREADS:
765 g_value_set_uint (value, self->max_threads);
767 case PROP_IGNORE_INACTIVE_PADS:
768 g_value_set_boolean (value,
769 gst_aggregator_get_ignore_inactive_pads (GST_AGGREGATOR (object)));
772 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
778 gst_compositor_set_property (GObject * object,
779 guint prop_id, const GValue * value, GParamSpec * pspec)
781 GstCompositor *self = GST_COMPOSITOR (object);
784 case PROP_BACKGROUND:
785 self->background = g_value_get_enum (value);
787 case PROP_ZERO_SIZE_IS_UNSCALED:
788 self->zero_size_is_unscaled = g_value_get_boolean (value);
790 case PROP_MAX_THREADS:
791 self->max_threads = g_value_get_uint (value);
793 case PROP_IGNORE_INACTIVE_PADS:
794 gst_aggregator_set_ignore_inactive_pads (GST_AGGREGATOR (object),
795 g_value_get_boolean (value));
798 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
803 #define gst_compositor_parent_class parent_class
804 G_DEFINE_TYPE_WITH_CODE (GstCompositor, gst_compositor,
805 GST_TYPE_VIDEO_AGGREGATOR, G_IMPLEMENT_INTERFACE (GST_TYPE_CHILD_PROXY,
806 gst_compositor_child_proxy_init));
807 GST_ELEMENT_REGISTER_DEFINE (compositor, "compositor", GST_RANK_PRIMARY + 1,
808 GST_TYPE_COMPOSITOR);
811 set_functions (GstCompositor * self, const GstVideoInfo * info)
813 gint offset[GST_VIDEO_MAX_COMPONENTS] = { 0, };
814 gint scale[GST_VIDEO_MAX_COMPONENTS] = { 0, };
818 self->overlay = NULL;
819 self->fill_checker = NULL;
820 self->fill_color = NULL;
822 switch (GST_VIDEO_INFO_FORMAT (info)) {
823 case GST_VIDEO_FORMAT_AYUV:
824 self->blend = gst_compositor_blend_ayuv;
825 self->overlay = gst_compositor_overlay_ayuv;
826 self->fill_checker = gst_compositor_fill_checker_ayuv;
827 self->fill_color = gst_compositor_fill_color_ayuv;
829 case GST_VIDEO_FORMAT_VUYA:
830 self->blend = gst_compositor_blend_vuya;
831 self->overlay = gst_compositor_overlay_vuya;
832 self->fill_checker = gst_compositor_fill_checker_vuya;
833 self->fill_color = gst_compositor_fill_color_vuya;
835 case GST_VIDEO_FORMAT_ARGB:
836 self->blend = gst_compositor_blend_argb;
837 self->overlay = gst_compositor_overlay_argb;
838 self->fill_checker = gst_compositor_fill_checker_argb;
839 self->fill_color = gst_compositor_fill_color_argb;
841 case GST_VIDEO_FORMAT_BGRA:
842 self->blend = gst_compositor_blend_bgra;
843 self->overlay = gst_compositor_overlay_bgra;
844 self->fill_checker = gst_compositor_fill_checker_bgra;
845 self->fill_color = gst_compositor_fill_color_bgra;
847 case GST_VIDEO_FORMAT_ABGR:
848 self->blend = gst_compositor_blend_abgr;
849 self->overlay = gst_compositor_overlay_abgr;
850 self->fill_checker = gst_compositor_fill_checker_abgr;
851 self->fill_color = gst_compositor_fill_color_abgr;
853 case GST_VIDEO_FORMAT_RGBA:
854 self->blend = gst_compositor_blend_rgba;
855 self->overlay = gst_compositor_overlay_rgba;
856 self->fill_checker = gst_compositor_fill_checker_rgba;
857 self->fill_color = gst_compositor_fill_color_rgba;
859 case GST_VIDEO_FORMAT_Y444:
860 self->blend = gst_compositor_blend_y444;
861 self->overlay = self->blend;
862 self->fill_checker = gst_compositor_fill_checker_y444;
863 self->fill_color = gst_compositor_fill_color_y444;
865 case GST_VIDEO_FORMAT_Y42B:
866 self->blend = gst_compositor_blend_y42b;
867 self->overlay = self->blend;
868 self->fill_checker = gst_compositor_fill_checker_y42b;
869 self->fill_color = gst_compositor_fill_color_y42b;
871 case GST_VIDEO_FORMAT_YUY2:
872 self->blend = gst_compositor_blend_yuy2;
873 self->overlay = self->blend;
874 self->fill_checker = gst_compositor_fill_checker_yuy2;
875 self->fill_color = gst_compositor_fill_color_yuy2;
877 case GST_VIDEO_FORMAT_UYVY:
878 self->blend = gst_compositor_blend_uyvy;
879 self->overlay = self->blend;
880 self->fill_checker = gst_compositor_fill_checker_uyvy;
881 self->fill_color = gst_compositor_fill_color_uyvy;
883 case GST_VIDEO_FORMAT_YVYU:
884 self->blend = gst_compositor_blend_yvyu;
885 self->overlay = self->blend;
886 self->fill_checker = gst_compositor_fill_checker_yvyu;
887 self->fill_color = gst_compositor_fill_color_yvyu;
889 case GST_VIDEO_FORMAT_I422_12LE:
890 self->blend = gst_compositor_blend_i422_12le;
891 self->overlay = self->blend;
892 self->fill_checker = gst_compositor_fill_checker_i422_12le;
893 self->fill_color = gst_compositor_fill_color_i422_12le;
895 case GST_VIDEO_FORMAT_I422_12BE:
896 self->blend = gst_compositor_blend_i422_12be;
897 self->overlay = self->blend;
898 self->fill_checker = gst_compositor_fill_checker_i422_12be;
899 self->fill_color = gst_compositor_fill_color_i422_12be;
901 case GST_VIDEO_FORMAT_I422_10LE:
902 self->blend = gst_compositor_blend_i422_10le;
903 self->overlay = self->blend;
904 self->fill_checker = gst_compositor_fill_checker_i422_10le;
905 self->fill_color = gst_compositor_fill_color_i422_10le;
907 case GST_VIDEO_FORMAT_I422_10BE:
908 self->blend = gst_compositor_blend_i422_10be;
909 self->overlay = self->blend;
910 self->fill_checker = gst_compositor_fill_checker_i422_10be;
911 self->fill_color = gst_compositor_fill_color_i422_10be;
913 case GST_VIDEO_FORMAT_I420_12LE:
914 self->blend = gst_compositor_blend_i420_12le;
915 self->overlay = self->blend;
916 self->fill_checker = gst_compositor_fill_checker_i420_12le;
917 self->fill_color = gst_compositor_fill_color_i420_12le;
919 case GST_VIDEO_FORMAT_I420_12BE:
920 self->blend = gst_compositor_blend_i420_12be;
921 self->overlay = self->blend;
922 self->fill_checker = gst_compositor_fill_checker_i420_12be;
923 self->fill_color = gst_compositor_fill_color_i420_12be;
925 case GST_VIDEO_FORMAT_I420_10LE:
926 self->blend = gst_compositor_blend_i420_10le;
927 self->overlay = self->blend;
928 self->fill_checker = gst_compositor_fill_checker_i420_10le;
929 self->fill_color = gst_compositor_fill_color_i420_10le;
931 case GST_VIDEO_FORMAT_I420_10BE:
932 self->blend = gst_compositor_blend_i420_10be;
933 self->overlay = self->blend;
934 self->fill_checker = gst_compositor_fill_checker_i420_10be;
935 self->fill_color = gst_compositor_fill_color_i420_10be;
937 case GST_VIDEO_FORMAT_I420:
938 self->blend = gst_compositor_blend_i420;
939 self->overlay = self->blend;
940 self->fill_checker = gst_compositor_fill_checker_i420;
941 self->fill_color = gst_compositor_fill_color_i420;
943 case GST_VIDEO_FORMAT_YV12:
944 self->blend = gst_compositor_blend_yv12;
945 self->overlay = self->blend;
946 self->fill_checker = gst_compositor_fill_checker_yv12;
947 self->fill_color = gst_compositor_fill_color_yv12;
949 case GST_VIDEO_FORMAT_NV12:
950 self->blend = gst_compositor_blend_nv12;
951 self->overlay = self->blend;
952 self->fill_checker = gst_compositor_fill_checker_nv12;
953 self->fill_color = gst_compositor_fill_color_nv12;
955 case GST_VIDEO_FORMAT_NV21:
956 self->blend = gst_compositor_blend_nv21;
957 self->overlay = self->blend;
958 self->fill_checker = gst_compositor_fill_checker_nv21;
959 self->fill_color = gst_compositor_fill_color_nv21;
961 case GST_VIDEO_FORMAT_Y41B:
962 self->blend = gst_compositor_blend_y41b;
963 self->overlay = self->blend;
964 self->fill_checker = gst_compositor_fill_checker_y41b;
965 self->fill_color = gst_compositor_fill_color_y41b;
967 case GST_VIDEO_FORMAT_RGB:
968 self->blend = gst_compositor_blend_rgb;
969 self->overlay = self->blend;
970 self->fill_checker = gst_compositor_fill_checker_rgb;
971 self->fill_color = gst_compositor_fill_color_rgb;
973 case GST_VIDEO_FORMAT_BGR:
974 self->blend = gst_compositor_blend_bgr;
975 self->overlay = self->blend;
976 self->fill_checker = gst_compositor_fill_checker_bgr;
977 self->fill_color = gst_compositor_fill_color_bgr;
979 case GST_VIDEO_FORMAT_xRGB:
980 self->blend = gst_compositor_blend_xrgb;
981 self->overlay = self->blend;
982 self->fill_checker = gst_compositor_fill_checker_xrgb;
983 self->fill_color = gst_compositor_fill_color_xrgb;
985 case GST_VIDEO_FORMAT_xBGR:
986 self->blend = gst_compositor_blend_xbgr;
987 self->overlay = self->blend;
988 self->fill_checker = gst_compositor_fill_checker_xbgr;
989 self->fill_color = gst_compositor_fill_color_xbgr;
991 case GST_VIDEO_FORMAT_RGBx:
992 self->blend = gst_compositor_blend_rgbx;
993 self->overlay = self->blend;
994 self->fill_checker = gst_compositor_fill_checker_rgbx;
995 self->fill_color = gst_compositor_fill_color_rgbx;
997 case GST_VIDEO_FORMAT_BGRx:
998 self->blend = gst_compositor_blend_bgrx;
999 self->overlay = self->blend;
1000 self->fill_checker = gst_compositor_fill_checker_bgrx;
1001 self->fill_color = gst_compositor_fill_color_bgrx;
1004 GST_ERROR_OBJECT (self, "Unhandled format %s",
1005 gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (info)));
1009 /* calculate black and white colors */
1010 gst_video_color_range_offsets (info->colorimetry.range, info->finfo,
1012 if (GST_VIDEO_INFO_IS_YUV (info)) {
1013 /* black color [0.0, 0.0, 0.0] */
1014 self->black_color[0] = offset[0];
1016 /* white color [1.0, 0.0, 0.0] */
1017 self->white_color[0] = scale[0] + offset[0];
1019 for (i = 1; i < 3; i++)
1020 self->black_color[i] = self->white_color[i] = offset[i];
1022 for (i = 0; i < 3; i++) {
1023 self->black_color[i] = offset[i];
1024 self->white_color[i] = scale[i] + offset[i];
1028 GST_DEBUG_OBJECT (self,
1029 "Calculated background color block: [%d %d %d], white: [%d %d %d]",
1030 self->black_color[0], self->black_color[1], self->black_color[2],
1031 self->white_color[0], self->white_color[1], self->white_color[2]);
1037 _fixate_caps (GstAggregator * agg, GstCaps * caps)
1039 GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
1041 gint best_width = -1, best_height = -1;
1042 gint best_fps_n = -1, best_fps_d = -1;
1044 gdouble best_fps = 0.;
1045 GstCaps *ret = NULL;
1048 ret = gst_caps_make_writable (caps);
1050 /* we need this to calculate how large to make the output frame */
1051 s = gst_caps_get_structure (ret, 0);
1052 if (gst_structure_has_field (s, "pixel-aspect-ratio")) {
1053 gst_structure_fixate_field_nearest_fraction (s, "pixel-aspect-ratio", 1, 1);
1054 gst_structure_get_fraction (s, "pixel-aspect-ratio", &par_n, &par_d);
1059 GST_OBJECT_LOCK (vagg);
1060 for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
1061 GstVideoAggregatorPad *vaggpad = l->data;
1062 GstCompositorPad *compositor_pad = GST_COMPOSITOR_PAD (vaggpad);
1063 gint this_width, this_height;
1070 if (gst_aggregator_pad_is_inactive (GST_AGGREGATOR_PAD (vaggpad)))
1073 fps_n = GST_VIDEO_INFO_FPS_N (&vaggpad->info);
1074 fps_d = GST_VIDEO_INFO_FPS_D (&vaggpad->info);
1075 _mixer_pad_get_output_size (GST_COMPOSITOR (vagg), compositor_pad, par_n,
1076 par_d, &width, &height, &x_offset, &y_offset);
1078 if (width == 0 || height == 0)
1081 /* {x,y}_offset represent padding size of each top and left area.
1082 * To calculate total resolution, count bottom and right padding area
1084 this_width = width + MAX (compositor_pad->xpos + 2 * x_offset, 0);
1085 this_height = height + MAX (compositor_pad->ypos + 2 * y_offset, 0);
1087 if (best_width < this_width)
1088 best_width = this_width;
1089 if (best_height < this_height)
1090 best_height = this_height;
1095 gst_util_fraction_to_double (fps_n, fps_d, &cur_fps);
1097 if (best_fps < cur_fps) {
1103 GST_OBJECT_UNLOCK (vagg);
1105 if (best_fps_n <= 0 || best_fps_d <= 0 || best_fps == 0.0) {
1111 gst_structure_fixate_field_nearest_int (s, "width", best_width);
1112 gst_structure_fixate_field_nearest_int (s, "height", best_height);
1113 gst_structure_fixate_field_nearest_fraction (s, "framerate", best_fps_n,
1115 ret = gst_caps_fixate (ret);
1121 gst_parallelized_task_thread_func (gpointer data)
1123 GstParallelizedTaskRunner *runner = data;
1126 g_mutex_lock (&runner->lock);
1127 idx = runner->n_todo--;
1128 g_assert (runner->n_todo >= -1);
1129 g_mutex_unlock (&runner->lock);
1131 g_assert (runner->func != NULL);
1133 runner->func (runner->task_data[idx]);
1137 gst_parallelized_task_runner_join (GstParallelizedTaskRunner * self)
1139 gboolean joined = FALSE;
1142 g_mutex_lock (&self->lock);
1143 if (!(joined = gst_queue_array_is_empty (self->tasks))) {
1144 gpointer task = gst_queue_array_pop_head (self->tasks);
1145 g_mutex_unlock (&self->lock);
1146 gst_task_pool_join (self->pool, task);
1148 g_mutex_unlock (&self->lock);
1154 gst_parallelized_task_runner_free (GstParallelizedTaskRunner * self)
1156 gst_parallelized_task_runner_join (self);
1158 gst_queue_array_free (self->tasks);
1160 gst_task_pool_cleanup (self->pool);
1161 gst_object_unref (self->pool);
1162 g_mutex_clear (&self->lock);
1166 static GstParallelizedTaskRunner *
1167 gst_parallelized_task_runner_new (guint n_threads, GstTaskPool * pool,
1168 gboolean async_tasks)
1170 GstParallelizedTaskRunner *self;
1173 n_threads = g_get_num_processors ();
1175 self = g_new0 (GstParallelizedTaskRunner, 1);
1178 self->pool = g_object_ref (pool);
1179 self->own_pool = FALSE;
1181 /* No reason to split up the work between more threads than the
1183 if (GST_IS_SHARED_TASK_POOL (pool))
1186 gst_shared_task_pool_get_max_threads (GST_SHARED_TASK_POOL (pool)));
1188 self->pool = gst_shared_task_pool_new ();
1189 self->own_pool = TRUE;
1190 gst_shared_task_pool_set_max_threads (GST_SHARED_TASK_POOL (self->pool),
1192 gst_task_pool_prepare (self->pool, NULL);
1195 self->tasks = gst_queue_array_new (n_threads);
1197 self->n_threads = n_threads;
1200 g_mutex_init (&self->lock);
1202 /* Set when scheduling a job */
1204 self->task_data = NULL;
1205 self->async_tasks = async_tasks;
1211 gst_parallelized_task_runner_finish (GstParallelizedTaskRunner * self)
1213 g_return_if_fail (self->func != NULL);
1215 gst_parallelized_task_runner_join (self);
1218 self->task_data = NULL;
1222 gst_parallelized_task_runner_run (GstParallelizedTaskRunner * self,
1223 GstParallelizedTaskFunc func, gpointer * task_data)
1225 guint n_threads = self->n_threads;
1228 self->task_data = task_data;
1230 if (n_threads > 1 || self->async_tasks) {
1232 g_mutex_lock (&self->lock);
1233 self->n_todo = self->n_threads - 1;
1234 if (!self->async_tasks) {
1235 /* if not async, perform one of the functions in the current thread */
1239 for (; i < n_threads; i++) {
1241 gst_task_pool_push (self->pool, gst_parallelized_task_thread_func,
1244 /* The return value of push() is nullable but NULL is only returned
1245 * with the shared task pool when gst_task_pool_prepare() has not been
1246 * called and would thus be a programming error that we should hard-fail
1249 g_assert (task != NULL);
1250 gst_queue_array_push_tail (self->tasks, task);
1252 g_mutex_unlock (&self->lock);
1255 if (!self->async_tasks) {
1256 self->func (self->task_data[self->n_threads - 1]);
1258 gst_parallelized_task_runner_finish (self);
1263 _negotiated_caps (GstAggregator * agg, GstCaps * caps)
1265 GstCompositor *compositor = GST_COMPOSITOR (agg);
1266 GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
1267 GstVideoInfo v_info;
1270 GST_DEBUG_OBJECT (agg, "Negotiated caps %" GST_PTR_FORMAT, caps);
1272 if (!gst_video_info_from_caps (&v_info, caps))
1275 if (!set_functions (compositor, &v_info)) {
1276 GST_ERROR_OBJECT (agg, "Failed to setup vfuncs");
1280 if (compositor->max_threads == 0)
1281 n_threads = g_get_num_processors ();
1283 n_threads = compositor->max_threads;
1285 /* Magic number of 200 lines */
1286 if (GST_VIDEO_INFO_HEIGHT (&v_info) / n_threads < 200)
1287 n_threads = (GST_VIDEO_INFO_HEIGHT (&v_info) + 199) / 200;
1291 /* XXX: implement better thread count change */
1292 if (compositor->blend_runner
1293 && compositor->blend_runner->n_threads != n_threads) {
1294 gst_parallelized_task_runner_free (compositor->blend_runner);
1295 compositor->blend_runner = NULL;
1297 if (!compositor->blend_runner) {
1298 GstTaskPool *pool = gst_video_aggregator_get_execution_task_pool (vagg);
1299 compositor->blend_runner =
1300 gst_parallelized_task_runner_new (n_threads, pool, FALSE);
1301 gst_clear_object (&pool);
1304 return GST_AGGREGATOR_CLASS (parent_class)->negotiated_src_caps (agg, caps);
1308 _should_draw_background (GstVideoAggregator * vagg)
1310 GstVideoRectangle bg_rect;
1311 gboolean draw = TRUE;
1314 bg_rect.x = bg_rect.y = 0;
1316 GST_OBJECT_LOCK (vagg);
1317 bg_rect.w = GST_VIDEO_INFO_WIDTH (&vagg->info);
1318 bg_rect.h = GST_VIDEO_INFO_HEIGHT (&vagg->info);
1319 /* Check if the background is completely obscured by a pad
1320 * TODO: Also skip if it's obscured by a combination of pads */
1321 for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
1322 if (gst_aggregator_pad_is_inactive (GST_AGGREGATOR_PAD (l->data))
1324 gst_video_aggregator_pad_get_prepared_frame (GST_VIDEO_AGGREGATOR_PAD
1328 if (_pad_obscures_rectangle (vagg, l->data, bg_rect)) {
1333 GST_OBJECT_UNLOCK (vagg);
1338 frames_can_copy (const GstVideoFrame * frame1, const GstVideoFrame * frame2)
1340 if (GST_VIDEO_FRAME_FORMAT (frame1) != GST_VIDEO_FRAME_FORMAT (frame2))
1342 if (GST_VIDEO_FRAME_HEIGHT (frame1) != GST_VIDEO_FRAME_HEIGHT (frame2))
1344 if (GST_VIDEO_FRAME_WIDTH (frame1) != GST_VIDEO_FRAME_WIDTH (frame2))
1349 struct CompositePadInfo
1351 GstVideoFrame *prepared_frame;
1352 GstCompositorPad *pad;
1353 GstCompositorBlendMode blend_mode;
1356 struct CompositeTask
1358 GstCompositor *compositor;
1359 GstVideoFrame *out_frame;
1360 guint dst_line_start;
1362 gboolean draw_background;
1364 struct CompositePadInfo *pads_info;
1368 _draw_background (GstCompositor * comp, GstVideoFrame * outframe,
1369 guint y_start, guint y_end, BlendFunction * composite)
1371 *composite = comp->blend;
1373 switch (comp->background) {
1374 case COMPOSITOR_BACKGROUND_CHECKER:
1375 comp->fill_checker (outframe, y_start, y_end);
1377 case COMPOSITOR_BACKGROUND_BLACK:
1378 comp->fill_color (outframe, y_start, y_end,
1379 comp->black_color[GST_VIDEO_COMP_Y],
1380 comp->black_color[GST_VIDEO_COMP_U],
1381 comp->black_color[GST_VIDEO_COMP_V]);
1383 case COMPOSITOR_BACKGROUND_WHITE:
1384 comp->fill_color (outframe, y_start, y_end,
1385 comp->white_color[GST_VIDEO_COMP_Y],
1386 comp->white_color[GST_VIDEO_COMP_U],
1387 comp->white_color[GST_VIDEO_COMP_V]);
1389 case COMPOSITOR_BACKGROUND_TRANSPARENT:
1391 guint i, plane, num_planes, height;
1393 num_planes = GST_VIDEO_FRAME_N_PLANES (outframe);
1394 for (plane = 0; plane < num_planes; ++plane) {
1395 const GstVideoFormatInfo *info;
1396 gint comp[GST_VIDEO_MAX_COMPONENTS];
1398 gsize rowsize, plane_stride;
1401 info = outframe->info.finfo;
1402 pdata = GST_VIDEO_FRAME_PLANE_DATA (outframe, plane);
1403 plane_stride = GST_VIDEO_FRAME_PLANE_STRIDE (outframe, plane);
1405 gst_video_format_info_component (info, plane, comp);
1406 rowsize = GST_VIDEO_FRAME_COMP_WIDTH (outframe, comp[0])
1407 * GST_VIDEO_FRAME_COMP_PSTRIDE (outframe, comp[0]);
1408 height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, comp[0],
1411 yoffset = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, comp[0], y_start);
1413 pdata += yoffset * plane_stride;
1414 for (i = 0; i < height; ++i) {
1415 memset (pdata, 0, rowsize);
1416 pdata += plane_stride;
1419 /* use overlay to keep background transparent */
1420 *composite = comp->overlay;
1427 blend_pads (struct CompositeTask *comp)
1429 BlendFunction composite;
1432 composite = comp->compositor->blend;
1434 if (comp->draw_background) {
1435 _draw_background (comp->compositor, comp->out_frame, comp->dst_line_start,
1436 comp->dst_line_end, &composite);
1439 for (i = 0; i < comp->n_pads; i++) {
1440 composite (comp->pads_info[i].prepared_frame,
1441 comp->pads_info[i].pad->xpos + comp->pads_info[i].pad->x_offset,
1442 comp->pads_info[i].pad->ypos + comp->pads_info[i].pad->y_offset,
1443 comp->pads_info[i].pad->alpha, comp->out_frame, comp->dst_line_start,
1444 comp->dst_line_end, comp->pads_info[i].blend_mode);
1448 static GstFlowReturn
1449 gst_compositor_aggregate_frames (GstVideoAggregator * vagg, GstBuffer * outbuf)
1451 GstCompositor *compositor = GST_COMPOSITOR (vagg);
1453 GstVideoFrame out_frame, *outframe;
1454 gboolean draw_background;
1455 guint drawn_a_pad = FALSE;
1456 struct CompositePadInfo *pads_info;
1457 guint i, n_pads = 0;
1459 if (!gst_video_frame_map (&out_frame, &vagg->info, outbuf, GST_MAP_WRITE)) {
1460 GST_WARNING_OBJECT (vagg, "Could not map output buffer");
1461 return GST_FLOW_ERROR;
1464 outframe = &out_frame;
1466 /* If one of the frames to be composited completely obscures the background,
1467 * don't bother drawing the background at all. We can also always use the
1468 * 'blend' BlendFunction in that case because it only changes if we have to
1469 * overlay on top of a transparent background. */
1470 draw_background = _should_draw_background (vagg);
1472 GST_OBJECT_LOCK (vagg);
1473 for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
1474 GstVideoAggregatorPad *pad = l->data;
1475 GstVideoFrame *prepared_frame =
1476 gst_video_aggregator_pad_get_prepared_frame (pad);
1482 /* If no prepared frame, we should draw background unconditionally in order
1483 * to clear output buffer */
1485 draw_background = TRUE;
1487 pads_info = g_newa (struct CompositePadInfo, n_pads);
1490 for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
1491 GstVideoAggregatorPad *pad = l->data;
1492 GstCompositorPad *compo_pad = GST_COMPOSITOR_PAD (pad);
1493 GstVideoFrame *prepared_frame =
1494 gst_video_aggregator_pad_get_prepared_frame (pad);
1495 GstCompositorBlendMode blend_mode = COMPOSITOR_BLEND_MODE_OVER;
1497 switch (compo_pad->op) {
1498 case COMPOSITOR_OPERATOR_SOURCE:
1499 blend_mode = COMPOSITOR_BLEND_MODE_SOURCE;
1501 case COMPOSITOR_OPERATOR_OVER:
1502 blend_mode = COMPOSITOR_BLEND_MODE_OVER;
1504 case COMPOSITOR_OPERATOR_ADD:
1505 blend_mode = COMPOSITOR_BLEND_MODE_ADD;
1508 g_assert_not_reached ();
1512 if (prepared_frame != NULL) {
1513 /* If this is the first pad we're drawing, and we didn't draw the
1514 * background, and @prepared_frame has the same format, height, and width
1515 * as @outframe, then we can just copy it as-is. Subsequent pads (if any)
1516 * will be composited on top of it. */
1517 if (!drawn_a_pad && !draw_background &&
1518 frames_can_copy (prepared_frame, outframe)) {
1519 gst_video_frame_copy (outframe, prepared_frame);
1521 pads_info[n_pads].pad = compo_pad;
1522 pads_info[n_pads].prepared_frame = prepared_frame;
1523 pads_info[n_pads].blend_mode = blend_mode;
1531 guint n_threads, lines_per_thread;
1533 struct CompositeTask *tasks;
1534 struct CompositeTask **tasks_p;
1536 n_threads = compositor->blend_runner->n_threads;
1538 tasks = g_newa (struct CompositeTask, n_threads);
1539 tasks_p = g_newa (struct CompositeTask *, n_threads);
1541 out_height = GST_VIDEO_FRAME_HEIGHT (outframe);
1542 lines_per_thread = (out_height + n_threads - 1) / n_threads;
1544 for (i = 0; i < n_threads; i++) {
1545 tasks[i].compositor = compositor;
1546 tasks[i].n_pads = n_pads;
1547 tasks[i].pads_info = pads_info;
1548 tasks[i].out_frame = outframe;
1549 tasks[i].draw_background = draw_background;
1550 /* This is a dumb split of the work by number of output lines.
1551 * If there is a section of the output that reads from a lot of source
1552 * pads, then that thread will consume more time. Maybe tracking and
1553 * splitting on the source fill rate would produce better results. */
1554 tasks[i].dst_line_start = i * lines_per_thread;
1555 tasks[i].dst_line_end = MIN ((i + 1) * lines_per_thread, out_height);
1557 tasks_p[i] = &tasks[i];
1560 gst_parallelized_task_runner_run (compositor->blend_runner,
1561 (GstParallelizedTaskFunc) blend_pads, (gpointer *) tasks_p);
1564 GST_OBJECT_UNLOCK (vagg);
1566 gst_video_frame_unmap (outframe);
1572 gst_compositor_request_new_pad (GstElement * element, GstPadTemplate * templ,
1573 const gchar * req_name, const GstCaps * caps)
1578 GST_ELEMENT_CLASS (parent_class)->request_new_pad (element,
1579 templ, req_name, caps);
1582 goto could_not_create;
1584 gst_child_proxy_child_added (GST_CHILD_PROXY (element), G_OBJECT (newpad),
1585 GST_OBJECT_NAME (newpad));
1591 GST_DEBUG_OBJECT (element, "could not create/add pad");
1597 gst_compositor_release_pad (GstElement * element, GstPad * pad)
1599 GstCompositor *compositor;
1601 compositor = GST_COMPOSITOR (element);
1603 GST_DEBUG_OBJECT (compositor, "release pad %s:%s", GST_DEBUG_PAD_NAME (pad));
1605 gst_child_proxy_child_removed (GST_CHILD_PROXY (compositor), G_OBJECT (pad),
1606 GST_OBJECT_NAME (pad));
1608 GST_ELEMENT_CLASS (parent_class)->release_pad (element, pad);
1612 src_pad_mouse_event (GstElement * element, GstPad * pad, gpointer user_data)
1614 GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR_CAST (element);
1615 GstCompositor *comp = GST_COMPOSITOR (element);
1616 GstCompositorPad *cpad = GST_COMPOSITOR_PAD (pad);
1618 gst_structure_copy (gst_event_get_structure (GST_EVENT_CAST (user_data)));
1619 gdouble event_x, event_y;
1620 gint offset_x, offset_y;
1621 GstVideoRectangle rect;
1623 gst_structure_get (st, "pointer_x", G_TYPE_DOUBLE, &event_x,
1624 "pointer_y", G_TYPE_DOUBLE, &event_y, NULL);
1626 /* Find output rectangle of this pad */
1627 _mixer_pad_get_output_size (comp, cpad,
1628 GST_VIDEO_INFO_PAR_N (&vagg->info),
1629 GST_VIDEO_INFO_PAR_D (&vagg->info),
1630 &(rect.w), &(rect.h), &offset_x, &offset_y);
1631 rect.x = cpad->xpos + offset_x;
1632 rect.y = cpad->ypos + offset_y;
1634 /* Translate coordinates and send event if it lies in this rectangle */
1635 if (is_point_contained (rect, event_x, event_y)) {
1636 GstVideoAggregatorPad *vpad = GST_VIDEO_AGGREGATOR_PAD_CAST (cpad);
1639 w = (gdouble) GST_VIDEO_INFO_WIDTH (&vpad->info);
1640 h = (gdouble) GST_VIDEO_INFO_HEIGHT (&vpad->info);
1641 x = (event_x - (gdouble) rect.x) * (w / (gdouble) rect.w);
1642 y = (event_y - (gdouble) rect.y) * (h / (gdouble) rect.h);
1644 gst_structure_set (st, "pointer_x", G_TYPE_DOUBLE, x,
1645 "pointer_y", G_TYPE_DOUBLE, y, NULL);
1646 gst_pad_push_event (pad, gst_event_new_navigation (st));
1648 gst_structure_free (st);
1655 _src_event (GstAggregator * agg, GstEvent * event)
1657 GstNavigationEventType event_type;
1659 switch (GST_EVENT_TYPE (event)) {
1660 case GST_EVENT_NAVIGATION:
1662 event_type = gst_navigation_event_get_type (event);
1663 switch (event_type) {
1664 case GST_NAVIGATION_EVENT_MOUSE_BUTTON_PRESS:
1665 case GST_NAVIGATION_EVENT_MOUSE_BUTTON_RELEASE:
1666 case GST_NAVIGATION_EVENT_MOUSE_MOVE:
1667 case GST_NAVIGATION_EVENT_MOUSE_SCROLL:
1668 gst_element_foreach_sink_pad (GST_ELEMENT_CAST (agg),
1669 src_pad_mouse_event, event);
1670 gst_event_unref (event);
1681 return GST_AGGREGATOR_CLASS (parent_class)->src_event (agg, event);
1685 _sink_query (GstAggregator * agg, GstAggregatorPad * bpad, GstQuery * query)
1687 switch (GST_QUERY_TYPE (query)) {
1688 case GST_QUERY_ALLOCATION:{
1691 GstBufferPool *pool;
1693 GstStructure *structure;
1695 gst_query_parse_allocation (query, &caps, NULL);
1700 if (!gst_video_info_from_caps (&info, caps))
1703 size = GST_VIDEO_INFO_SIZE (&info);
1705 pool = gst_video_buffer_pool_new ();
1707 structure = gst_buffer_pool_get_config (pool);
1708 gst_buffer_pool_config_set_params (structure, caps, size, 0, 0);
1710 if (!gst_buffer_pool_set_config (pool, structure)) {
1711 gst_object_unref (pool);
1715 gst_query_add_allocation_pool (query, pool, size, 0, 0);
1716 gst_object_unref (pool);
1717 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
1722 return GST_AGGREGATOR_CLASS (parent_class)->sink_query (agg, bpad, query);
1727 gst_compositor_finalize (GObject * object)
1729 GstCompositor *compositor = GST_COMPOSITOR (object);
1731 if (compositor->blend_runner)
1732 gst_parallelized_task_runner_free (compositor->blend_runner);
1733 compositor->blend_runner = NULL;
1735 G_OBJECT_CLASS (parent_class)->finalize (object);
1738 /* GObject boilerplate */
1740 gst_compositor_class_init (GstCompositorClass * klass)
1742 GObjectClass *gobject_class = (GObjectClass *) klass;
1743 GstElementClass *gstelement_class = (GstElementClass *) klass;
1744 GstVideoAggregatorClass *videoaggregator_class =
1745 (GstVideoAggregatorClass *) klass;
1746 GstAggregatorClass *agg_class = (GstAggregatorClass *) klass;
1748 gobject_class->get_property = gst_compositor_get_property;
1749 gobject_class->set_property = gst_compositor_set_property;
1750 gobject_class->finalize = gst_compositor_finalize;
1752 gstelement_class->request_new_pad =
1753 GST_DEBUG_FUNCPTR (gst_compositor_request_new_pad);
1754 gstelement_class->release_pad =
1755 GST_DEBUG_FUNCPTR (gst_compositor_release_pad);
1756 agg_class->sink_query = _sink_query;
1757 agg_class->src_event = _src_event;
1758 agg_class->fixate_src_caps = _fixate_caps;
1759 agg_class->negotiated_src_caps = _negotiated_caps;
1760 videoaggregator_class->aggregate_frames = gst_compositor_aggregate_frames;
1762 g_object_class_install_property (gobject_class, PROP_BACKGROUND,
1763 g_param_spec_enum ("background", "Background", "Background type",
1764 GST_TYPE_COMPOSITOR_BACKGROUND,
1765 DEFAULT_BACKGROUND, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1768 * compositor:zero-size-is-unscaled:
1770 * Whether a pad with height or width 0 should be left unscaled
1771 * in that dimension, or simply not composited in. Setting it to
1772 * %FALSE might be useful when animating those properties.
1776 g_object_class_install_property (gobject_class, PROP_ZERO_SIZE_IS_UNSCALED,
1777 g_param_spec_boolean ("zero-size-is-unscaled", "Zero size is unscaled",
1778 "If TRUE, then input video is unscaled in that dimension "
1779 "if width or height is 0 (for backwards compatibility)",
1780 DEFAULT_ZERO_SIZE_IS_UNSCALED,
1781 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1784 * compositor:max-threads:
1786 * Maximum number of blending/rendering worker threads to spawn (0 = auto)
1790 g_object_class_install_property (gobject_class, PROP_MAX_THREADS,
1791 g_param_spec_uint ("max-threads", "Max Threads",
1792 "Maximum number of blending/rendering worker threads to spawn "
1793 "(0 = auto)", 0, G_MAXINT, DEFAULT_MAX_THREADS,
1794 GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE |
1795 G_PARAM_STATIC_STRINGS));
1797 gst_element_class_add_static_pad_template_with_gtype (gstelement_class,
1798 &src_factory, GST_TYPE_AGGREGATOR_PAD);
1799 gst_element_class_add_static_pad_template_with_gtype (gstelement_class,
1800 &sink_factory, GST_TYPE_COMPOSITOR_PAD);
1802 gst_element_class_set_static_metadata (gstelement_class, "Compositor",
1803 "Filter/Editor/Video/Compositor",
1804 "Composite multiple video streams", "Wim Taymans <wim@fluendo.com>, "
1805 "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
1808 * compositor:ignore-inactive-pads:
1810 * Don't wait for inactive pads when live. An inactive pad
1811 * is a pad that hasn't yet received a buffer, but that has
1812 * been waited on at least once.
1814 * The purpose of this property is to avoid aggregating on
1815 * timeout when new pads are requested in advance of receiving
1816 * data flow, for example the user may decide to connect it later,
1817 * but wants to configure it already.
1821 g_object_class_install_property (gobject_class,
1822 PROP_IGNORE_INACTIVE_PADS, g_param_spec_boolean ("ignore-inactive-pads",
1823 "Ignore inactive pads",
1824 "Avoid timing out waiting for inactive pads", FALSE,
1825 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1827 gst_type_mark_as_plugin_api (GST_TYPE_COMPOSITOR_PAD, 0);
1828 gst_type_mark_as_plugin_api (GST_TYPE_COMPOSITOR_OPERATOR, 0);
1829 gst_type_mark_as_plugin_api (GST_TYPE_COMPOSITOR_BACKGROUND, 0);
1833 gst_compositor_init (GstCompositor * self)
1835 /* initialize variables */
1836 self->background = DEFAULT_BACKGROUND;
1837 self->zero_size_is_unscaled = DEFAULT_ZERO_SIZE_IS_UNSCALED;
1838 self->max_threads = DEFAULT_MAX_THREADS;
1841 /* GstChildProxy implementation */
1843 gst_compositor_child_proxy_get_child_by_index (GstChildProxy * child_proxy,
1846 GstCompositor *compositor = GST_COMPOSITOR (child_proxy);
1847 GObject *obj = NULL;
1849 GST_OBJECT_LOCK (compositor);
1850 obj = g_list_nth_data (GST_ELEMENT_CAST (compositor)->sinkpads, index);
1852 gst_object_ref (obj);
1853 GST_OBJECT_UNLOCK (compositor);
1859 gst_compositor_child_proxy_get_children_count (GstChildProxy * child_proxy)
1862 GstCompositor *compositor = GST_COMPOSITOR (child_proxy);
1864 GST_OBJECT_LOCK (compositor);
1865 count = GST_ELEMENT_CAST (compositor)->numsinkpads;
1866 GST_OBJECT_UNLOCK (compositor);
1867 GST_INFO_OBJECT (compositor, "Children Count: %d", count);
1873 gst_compositor_child_proxy_init (gpointer g_iface, gpointer iface_data)
1875 GstChildProxyInterface *iface = g_iface;
1877 iface->get_child_by_index = gst_compositor_child_proxy_get_child_by_index;
1878 iface->get_children_count = gst_compositor_child_proxy_get_children_count;
1881 /* Element registration */
1883 plugin_init (GstPlugin * plugin)
1885 GST_DEBUG_CATEGORY_INIT (gst_compositor_debug, "compositor", 0, "compositor");
1887 gst_compositor_init_blend ();
1889 return GST_ELEMENT_REGISTER (compositor, plugin);
1892 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1895 "Compositor", plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME,