mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-22 08:17:01 +00:00
605 lines
20 KiB
C
605 lines
20 KiB
C
|
/* Video compositor plugin
|
||
|
* Copyright (C) 2004, 2008 Wim Taymans <wim@fluendo.com>
|
||
|
* Copyright (C) 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
|
||
|
* Copyright (C) 2014 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
|
||
|
* Copyright (C) 2014 Thibault Saunier <tsaunier@gnome.org>
|
||
|
*
|
||
|
* This library is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU Library General Public
|
||
|
* License as published by the Free Software Foundation; either
|
||
|
* version 2 of the License, or (at your option) any later version.
|
||
|
*
|
||
|
* This library is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
* Library General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU Library General Public
|
||
|
* License along with this library; if not, write to the
|
||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||
|
* Boston, MA 02110-1301, USA.
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* SECTION:element-compositor
|
||
|
*
|
||
|
* Compositor can accept AYUV, ARGB and BGRA video streams. For each of the requested
|
||
|
* sink pads it will compare the incoming geometry and framerate to define the
|
||
|
* output parameters. Indeed output video frames will have the geometry of the
|
||
|
* biggest incoming video stream and the framerate of the fastest incoming one.
|
||
|
*
|
||
|
* Compositor will do colorspace conversion.
|
||
|
*
|
||
|
* Individual parameters for each input stream can be configured on the
|
||
|
* #GstCompositorPad.
|
||
|
*
|
||
|
* <refsect2>
|
||
|
* <title>Sample pipelines</title>
|
||
|
* |[
|
||
|
* gst-launch-1.0 \
|
||
|
* videotestsrc pattern=1 ! \
|
||
|
* video/x-raw,format=AYUV,framerate=\(fraction\)10/1,width=100,height=100 ! \
|
||
|
* videobox border-alpha=0 top=-70 bottom=-70 right=-220 ! \
|
||
|
* compositor name=comp sink_0::alpha=0.7 sink_1::alpha=0.5 ! \
|
||
|
* videoconvert ! xvimagesink \
|
||
|
* videotestsrc ! \
|
||
|
* video/x-raw,format=AYUV,framerate=\(fraction\)5/1,width=320,height=240 ! comp.
|
||
|
* ]| A pipeline to demonstrate compositor used together with videobox.
|
||
|
* This should show a 320x240 pixels video test source with some transparency
|
||
|
* showing the background checker pattern. Another video test source with just
|
||
|
* the snow pattern of 100x100 pixels is overlayed on top of the first one on
|
||
|
* the left vertically centered with a small transparency showing the first
|
||
|
* video test source behind and the checker pattern under it. Note that the
|
||
|
* framerate of the output video is 10 frames per second.
|
||
|
* |[
|
||
|
* gst-launch-1.0 videotestsrc pattern=1 ! \
|
||
|
* video/x-raw, framerate=\(fraction\)10/1, width=100, height=100 ! \
|
||
|
* compositor name=comp ! videoconvert ! ximagesink \
|
||
|
* videotestsrc ! \
|
||
|
* video/x-raw, framerate=\(fraction\)5/1, width=320, height=240 ! comp.
|
||
|
* ]| A pipeline to demostrate bgra comping. (This does not demonstrate alpha blending).
|
||
|
* |[
|
||
|
* gst-launch-1.0 videotestsrc pattern=1 ! \
|
||
|
* video/x-raw,format =I420, framerate=\(fraction\)10/1, width=100, height=100 ! \
|
||
|
* compositor name=comp ! videoconvert ! ximagesink \
|
||
|
* videotestsrc ! \
|
||
|
* video/x-raw,format=I420, framerate=\(fraction\)5/1, width=320, height=240 ! comp.
|
||
|
* ]| A pipeline to test I420
|
||
|
* |[
|
||
|
* gst-launch-1.0 compositor name=comp sink_1::alpha=0.5 sink_1::xpos=50 sink_1::ypos=50 ! \
|
||
|
* videoconvert ! ximagesink \
|
||
|
* videotestsrc pattern=snow timestamp-offset=3000000000 ! \
|
||
|
* "video/x-raw,format=AYUV,width=640,height=480,framerate=(fraction)30/1" ! \
|
||
|
* timeoverlay ! queue2 ! comp. \
|
||
|
* videotestsrc pattern=smpte ! \
|
||
|
* "video/x-raw,format=AYUV,width=800,height=600,framerate=(fraction)10/1" ! \
|
||
|
* timeoverlay ! queue2 ! comp.
|
||
|
* ]| A pipeline to demonstrate synchronized compositing (the second stream starts after 3 seconds)
|
||
|
* </refsect2>
|
||
|
*/
|
||
|
|
||
|
#ifdef HAVE_CONFIG_H
|
||
|
#include "config.h"
|
||
|
#endif
|
||
|
|
||
|
#include <string.h>
|
||
|
|
||
|
#include "compositor.h"
|
||
|
#include "compositorpad.h"
|
||
|
|
||
|
#ifdef DISABLE_ORC
|
||
|
#define orc_memset memset
|
||
|
#else
|
||
|
#include <orc/orcfunctions.h>
|
||
|
#endif
|
||
|
|
||
|
GST_DEBUG_CATEGORY_STATIC (gst_compositor_debug);
|
||
|
#define GST_CAT_DEFAULT gst_compositor_debug
|
||
|
|
||
|
#define FORMATS " { AYUV, BGRA, ARGB, RGBA, ABGR, Y444, Y42B, YUY2, UYVY, "\
|
||
|
" YVYU, I420, YV12, NV12, NV21, Y41B, RGB, BGR, xRGB, xBGR, "\
|
||
|
" RGBx, BGRx } "
|
||
|
|
||
|
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
|
||
|
GST_PAD_SRC,
|
||
|
GST_PAD_ALWAYS,
|
||
|
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (FORMATS))
|
||
|
);
|
||
|
|
||
|
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%u",
|
||
|
GST_PAD_SINK,
|
||
|
GST_PAD_REQUEST,
|
||
|
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (FORMATS))
|
||
|
);
|
||
|
|
||
|
#define DEFAULT_PAD_ZORDER 0
|
||
|
#define DEFAULT_PAD_XPOS 0
|
||
|
#define DEFAULT_PAD_YPOS 0
|
||
|
#define DEFAULT_PAD_ALPHA 1.0
|
||
|
enum
|
||
|
{
|
||
|
PROP_PAD_0,
|
||
|
PROP_PAD_ZORDER,
|
||
|
PROP_PAD_XPOS,
|
||
|
PROP_PAD_YPOS,
|
||
|
PROP_PAD_ALPHA
|
||
|
};
|
||
|
|
||
|
G_DEFINE_TYPE (GstCompositorPad, gst_compositor_pad,
|
||
|
GST_TYPE_VIDEO_AGGREGATOR_PAD);
|
||
|
|
||
|
static void
|
||
|
gst_compositor_pad_get_property (GObject * object, guint prop_id,
|
||
|
GValue * value, GParamSpec * pspec)
|
||
|
{
|
||
|
GstCompositorPad *pad = GST_COMPOSITOR_PAD (object);
|
||
|
|
||
|
switch (prop_id) {
|
||
|
case PROP_PAD_ZORDER:
|
||
|
g_value_set_uint (value, pad->zorder);
|
||
|
break;
|
||
|
case PROP_PAD_XPOS:
|
||
|
g_value_set_int (value, pad->xpos);
|
||
|
break;
|
||
|
case PROP_PAD_YPOS:
|
||
|
g_value_set_int (value, pad->ypos);
|
||
|
break;
|
||
|
case PROP_PAD_ALPHA:
|
||
|
g_value_set_double (value, pad->alpha);
|
||
|
break;
|
||
|
default:
|
||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
gst_compositor_pad_set_property (GObject * object, guint prop_id,
|
||
|
const GValue * value, GParamSpec * pspec)
|
||
|
{
|
||
|
GstCompositorPad *pad = GST_COMPOSITOR_PAD (object);
|
||
|
|
||
|
switch (prop_id) {
|
||
|
case PROP_PAD_XPOS:
|
||
|
pad->xpos = g_value_get_int (value);
|
||
|
break;
|
||
|
case PROP_PAD_YPOS:
|
||
|
pad->ypos = g_value_get_int (value);
|
||
|
break;
|
||
|
case PROP_PAD_ALPHA:
|
||
|
pad->alpha = g_value_get_double (value);
|
||
|
break;
|
||
|
default:
|
||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
gst_compositor_pad_class_init (GstCompositorPadClass * klass)
|
||
|
{
|
||
|
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||
|
|
||
|
gobject_class->set_property = gst_compositor_pad_set_property;
|
||
|
gobject_class->get_property = gst_compositor_pad_get_property;
|
||
|
|
||
|
g_object_class_install_property (gobject_class, PROP_PAD_XPOS,
|
||
|
g_param_spec_int ("xpos", "X Position", "X Position of the picture",
|
||
|
G_MININT, G_MAXINT, DEFAULT_PAD_XPOS,
|
||
|
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
|
||
|
g_object_class_install_property (gobject_class, PROP_PAD_YPOS,
|
||
|
g_param_spec_int ("ypos", "Y Position", "Y Position of the picture",
|
||
|
G_MININT, G_MAXINT, DEFAULT_PAD_YPOS,
|
||
|
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
|
||
|
g_object_class_install_property (gobject_class, PROP_PAD_ALPHA,
|
||
|
g_param_spec_double ("alpha", "Alpha", "Alpha of the picture", 0.0, 1.0,
|
||
|
DEFAULT_PAD_ALPHA,
|
||
|
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
gst_compositor_pad_init (GstCompositorPad * compo_pad)
|
||
|
{
|
||
|
compo_pad->xpos = DEFAULT_PAD_XPOS;
|
||
|
compo_pad->ypos = DEFAULT_PAD_YPOS;
|
||
|
compo_pad->alpha = DEFAULT_PAD_ALPHA;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* GstCompositor */
|
||
|
#define DEFAULT_BACKGROUND COMPOSITOR_BACKGROUND_CHECKER
|
||
|
enum
|
||
|
{
|
||
|
PROP_0,
|
||
|
PROP_BACKGROUND
|
||
|
};
|
||
|
|
||
|
#define GST_TYPE_COMPOSITOR_BACKGROUND (gst_compositor_background_get_type())
|
||
|
static GType
|
||
|
gst_compositor_background_get_type (void)
|
||
|
{
|
||
|
static GType compositor_background_type = 0;
|
||
|
|
||
|
static const GEnumValue compositor_background[] = {
|
||
|
{COMPOSITOR_BACKGROUND_CHECKER, "Checker pattern", "checker"},
|
||
|
{COMPOSITOR_BACKGROUND_BLACK, "Black", "black"},
|
||
|
{COMPOSITOR_BACKGROUND_WHITE, "White", "white"},
|
||
|
{COMPOSITOR_BACKGROUND_TRANSPARENT,
|
||
|
"Transparent Background to enable further compositing", "transparent"},
|
||
|
{0, NULL, NULL},
|
||
|
};
|
||
|
|
||
|
if (!compositor_background_type) {
|
||
|
compositor_background_type =
|
||
|
g_enum_register_static ("GstCompositorBackground",
|
||
|
compositor_background);
|
||
|
}
|
||
|
return compositor_background_type;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
gst_compositor_get_property (GObject * object,
|
||
|
guint prop_id, GValue * value, GParamSpec * pspec)
|
||
|
{
|
||
|
GstCompositor *self = GST_COMPOSITOR (object);
|
||
|
|
||
|
switch (prop_id) {
|
||
|
case PROP_BACKGROUND:
|
||
|
g_value_set_enum (value, self->background);
|
||
|
break;
|
||
|
default:
|
||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
gst_compositor_set_property (GObject * object,
|
||
|
guint prop_id, const GValue * value, GParamSpec * pspec)
|
||
|
{
|
||
|
GstCompositor *self = GST_COMPOSITOR (object);
|
||
|
|
||
|
switch (prop_id) {
|
||
|
case PROP_BACKGROUND:
|
||
|
self->background = g_value_get_enum (value);
|
||
|
break;
|
||
|
default:
|
||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#define gst_compositor_parent_class parent_class
|
||
|
G_DEFINE_TYPE (GstCompositor, gst_compositor, GST_TYPE_VIDEO_AGGREGATOR);
|
||
|
|
||
|
static gboolean
|
||
|
set_functions (GstCompositor * self, GstVideoInfo * info)
|
||
|
{
|
||
|
gboolean ret = FALSE;
|
||
|
|
||
|
self->blend = NULL;
|
||
|
self->overlay = NULL;
|
||
|
self->fill_checker = NULL;
|
||
|
self->fill_color = NULL;
|
||
|
|
||
|
switch (GST_VIDEO_INFO_FORMAT (info)) {
|
||
|
case GST_VIDEO_FORMAT_AYUV:
|
||
|
self->blend = gst_compositor_blend_ayuv;
|
||
|
self->overlay = gst_compositor_overlay_ayuv;
|
||
|
self->fill_checker = gst_compositor_fill_checker_ayuv;
|
||
|
self->fill_color = gst_compositor_fill_color_ayuv;
|
||
|
ret = TRUE;
|
||
|
break;
|
||
|
case GST_VIDEO_FORMAT_ARGB:
|
||
|
self->blend = gst_compositor_blend_argb;
|
||
|
self->overlay = gst_compositor_overlay_argb;
|
||
|
self->fill_checker = gst_compositor_fill_checker_argb;
|
||
|
self->fill_color = gst_compositor_fill_color_argb;
|
||
|
ret = TRUE;
|
||
|
break;
|
||
|
case GST_VIDEO_FORMAT_BGRA:
|
||
|
self->blend = gst_compositor_blend_bgra;
|
||
|
self->overlay = gst_compositor_overlay_bgra;
|
||
|
self->fill_checker = gst_compositor_fill_checker_bgra;
|
||
|
self->fill_color = gst_compositor_fill_color_bgra;
|
||
|
ret = TRUE;
|
||
|
break;
|
||
|
case GST_VIDEO_FORMAT_ABGR:
|
||
|
self->blend = gst_compositor_blend_abgr;
|
||
|
self->overlay = gst_compositor_overlay_abgr;
|
||
|
self->fill_checker = gst_compositor_fill_checker_abgr;
|
||
|
self->fill_color = gst_compositor_fill_color_abgr;
|
||
|
ret = TRUE;
|
||
|
break;
|
||
|
case GST_VIDEO_FORMAT_RGBA:
|
||
|
self->blend = gst_compositor_blend_rgba;
|
||
|
self->overlay = gst_compositor_overlay_rgba;
|
||
|
self->fill_checker = gst_compositor_fill_checker_rgba;
|
||
|
self->fill_color = gst_compositor_fill_color_rgba;
|
||
|
ret = TRUE;
|
||
|
break;
|
||
|
case GST_VIDEO_FORMAT_Y444:
|
||
|
self->blend = gst_compositor_blend_y444;
|
||
|
self->overlay = self->blend;
|
||
|
self->fill_checker = gst_compositor_fill_checker_y444;
|
||
|
self->fill_color = gst_compositor_fill_color_y444;
|
||
|
ret = TRUE;
|
||
|
break;
|
||
|
case GST_VIDEO_FORMAT_Y42B:
|
||
|
self->blend = gst_compositor_blend_y42b;
|
||
|
self->overlay = self->blend;
|
||
|
self->fill_checker = gst_compositor_fill_checker_y42b;
|
||
|
self->fill_color = gst_compositor_fill_color_y42b;
|
||
|
ret = TRUE;
|
||
|
break;
|
||
|
case GST_VIDEO_FORMAT_YUY2:
|
||
|
self->blend = gst_compositor_blend_yuy2;
|
||
|
self->overlay = self->blend;
|
||
|
self->fill_checker = gst_compositor_fill_checker_yuy2;
|
||
|
self->fill_color = gst_compositor_fill_color_yuy2;
|
||
|
ret = TRUE;
|
||
|
break;
|
||
|
case GST_VIDEO_FORMAT_UYVY:
|
||
|
self->blend = gst_compositor_blend_uyvy;
|
||
|
self->overlay = self->blend;
|
||
|
self->fill_checker = gst_compositor_fill_checker_uyvy;
|
||
|
self->fill_color = gst_compositor_fill_color_uyvy;
|
||
|
ret = TRUE;
|
||
|
break;
|
||
|
case GST_VIDEO_FORMAT_YVYU:
|
||
|
self->blend = gst_compositor_blend_yvyu;
|
||
|
self->overlay = self->blend;
|
||
|
self->fill_checker = gst_compositor_fill_checker_yvyu;
|
||
|
self->fill_color = gst_compositor_fill_color_yvyu;
|
||
|
ret = TRUE;
|
||
|
break;
|
||
|
case GST_VIDEO_FORMAT_I420:
|
||
|
self->blend = gst_compositor_blend_i420;
|
||
|
self->overlay = self->blend;
|
||
|
self->fill_checker = gst_compositor_fill_checker_i420;
|
||
|
self->fill_color = gst_compositor_fill_color_i420;
|
||
|
ret = TRUE;
|
||
|
break;
|
||
|
case GST_VIDEO_FORMAT_YV12:
|
||
|
self->blend = gst_compositor_blend_yv12;
|
||
|
self->overlay = self->blend;
|
||
|
self->fill_checker = gst_compositor_fill_checker_yv12;
|
||
|
self->fill_color = gst_compositor_fill_color_yv12;
|
||
|
ret = TRUE;
|
||
|
break;
|
||
|
case GST_VIDEO_FORMAT_NV12:
|
||
|
self->blend = gst_compositor_blend_nv12;
|
||
|
self->overlay = self->blend;
|
||
|
self->fill_checker = gst_compositor_fill_checker_nv12;
|
||
|
self->fill_color = gst_compositor_fill_color_nv12;
|
||
|
ret = TRUE;
|
||
|
break;
|
||
|
case GST_VIDEO_FORMAT_NV21:
|
||
|
self->blend = gst_compositor_blend_nv21;
|
||
|
self->overlay = self->blend;
|
||
|
self->fill_checker = gst_compositor_fill_checker_nv21;
|
||
|
self->fill_color = gst_compositor_fill_color_nv21;
|
||
|
ret = TRUE;
|
||
|
break;
|
||
|
case GST_VIDEO_FORMAT_Y41B:
|
||
|
self->blend = gst_compositor_blend_y41b;
|
||
|
self->overlay = self->blend;
|
||
|
self->fill_checker = gst_compositor_fill_checker_y41b;
|
||
|
self->fill_color = gst_compositor_fill_color_y41b;
|
||
|
ret = TRUE;
|
||
|
break;
|
||
|
case GST_VIDEO_FORMAT_RGB:
|
||
|
self->blend = gst_compositor_blend_rgb;
|
||
|
self->overlay = self->blend;
|
||
|
self->fill_checker = gst_compositor_fill_checker_rgb;
|
||
|
self->fill_color = gst_compositor_fill_color_rgb;
|
||
|
ret = TRUE;
|
||
|
break;
|
||
|
case GST_VIDEO_FORMAT_BGR:
|
||
|
self->blend = gst_compositor_blend_bgr;
|
||
|
self->overlay = self->blend;
|
||
|
self->fill_checker = gst_compositor_fill_checker_bgr;
|
||
|
self->fill_color = gst_compositor_fill_color_bgr;
|
||
|
ret = TRUE;
|
||
|
break;
|
||
|
case GST_VIDEO_FORMAT_xRGB:
|
||
|
self->blend = gst_compositor_blend_xrgb;
|
||
|
self->overlay = self->blend;
|
||
|
self->fill_checker = gst_compositor_fill_checker_xrgb;
|
||
|
self->fill_color = gst_compositor_fill_color_xrgb;
|
||
|
ret = TRUE;
|
||
|
break;
|
||
|
case GST_VIDEO_FORMAT_xBGR:
|
||
|
self->blend = gst_compositor_blend_xbgr;
|
||
|
self->overlay = self->blend;
|
||
|
self->fill_checker = gst_compositor_fill_checker_xbgr;
|
||
|
self->fill_color = gst_compositor_fill_color_xbgr;
|
||
|
ret = TRUE;
|
||
|
break;
|
||
|
case GST_VIDEO_FORMAT_RGBx:
|
||
|
self->blend = gst_compositor_blend_rgbx;
|
||
|
self->overlay = self->blend;
|
||
|
self->fill_checker = gst_compositor_fill_checker_rgbx;
|
||
|
self->fill_color = gst_compositor_fill_color_rgbx;
|
||
|
ret = TRUE;
|
||
|
break;
|
||
|
case GST_VIDEO_FORMAT_BGRx:
|
||
|
self->blend = gst_compositor_blend_bgrx;
|
||
|
self->overlay = self->blend;
|
||
|
self->fill_checker = gst_compositor_fill_checker_bgrx;
|
||
|
self->fill_color = gst_compositor_fill_color_bgrx;
|
||
|
ret = TRUE;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static gboolean
|
||
|
_update_info (GstVideoAggregator * vagg, GstVideoInfo * info)
|
||
|
{
|
||
|
GList *l;
|
||
|
gint best_width = -1, best_height = -1;
|
||
|
gboolean ret = FALSE;
|
||
|
|
||
|
GST_OBJECT_LOCK (vagg);
|
||
|
for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
|
||
|
GstVideoAggregatorPad *vaggpad = l->data;
|
||
|
GstCompositorPad *compositor_pad = GST_COMPOSITOR_PAD (vaggpad);
|
||
|
gint this_width, this_height;
|
||
|
gint width, height;
|
||
|
|
||
|
width = GST_VIDEO_INFO_WIDTH (&vaggpad->info);
|
||
|
height = GST_VIDEO_INFO_HEIGHT (&vaggpad->info);
|
||
|
|
||
|
if (width == 0 || height == 0)
|
||
|
continue;
|
||
|
|
||
|
this_width = width + MAX (compositor_pad->xpos, 0);
|
||
|
this_height = height + MAX (compositor_pad->ypos, 0);
|
||
|
|
||
|
if (best_width < this_width)
|
||
|
best_width = this_width;
|
||
|
if (best_height < this_height)
|
||
|
best_height = this_height;
|
||
|
}
|
||
|
GST_OBJECT_UNLOCK (vagg);
|
||
|
|
||
|
if (best_width > 0 && best_height > 0) {
|
||
|
gst_video_info_set_format (info, GST_VIDEO_INFO_FORMAT (info),
|
||
|
best_width, best_height);
|
||
|
ret = set_functions (GST_COMPOSITOR (vagg), info);
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static GstFlowReturn
|
||
|
gst_compositor_aggregate_frames (GstVideoAggregator * vagg, GstBuffer * outbuf)
|
||
|
{
|
||
|
GList *l;
|
||
|
GstCompositor *self = GST_COMPOSITOR (vagg);
|
||
|
BlendFunction composite;
|
||
|
GstVideoFrame out_frame, *outframe;
|
||
|
|
||
|
if (!gst_video_frame_map (&out_frame, &vagg->info, outbuf, GST_MAP_WRITE)) {
|
||
|
|
||
|
return GST_FLOW_ERROR;
|
||
|
}
|
||
|
|
||
|
outframe = &out_frame;
|
||
|
/* default to blending */
|
||
|
composite = self->blend;
|
||
|
switch (self->background) {
|
||
|
case COMPOSITOR_BACKGROUND_CHECKER:
|
||
|
self->fill_checker (outframe);
|
||
|
break;
|
||
|
case COMPOSITOR_BACKGROUND_BLACK:
|
||
|
self->fill_color (outframe, 16, 128, 128);
|
||
|
break;
|
||
|
case COMPOSITOR_BACKGROUND_WHITE:
|
||
|
self->fill_color (outframe, 240, 128, 128);
|
||
|
break;
|
||
|
case COMPOSITOR_BACKGROUND_TRANSPARENT:
|
||
|
{
|
||
|
guint i, plane, num_planes, height;
|
||
|
|
||
|
num_planes = GST_VIDEO_FRAME_N_PLANES (outframe);
|
||
|
for (plane = 0; plane < num_planes; ++plane) {
|
||
|
guint8 *pdata;
|
||
|
gsize rowsize, plane_stride;
|
||
|
|
||
|
pdata = GST_VIDEO_FRAME_PLANE_DATA (outframe, plane);
|
||
|
plane_stride = GST_VIDEO_FRAME_PLANE_STRIDE (outframe, plane);
|
||
|
rowsize = GST_VIDEO_FRAME_COMP_WIDTH (outframe, plane)
|
||
|
* GST_VIDEO_FRAME_COMP_PSTRIDE (outframe, plane);
|
||
|
height = GST_VIDEO_FRAME_COMP_HEIGHT (outframe, plane);
|
||
|
for (i = 0; i < height; ++i) {
|
||
|
memset (pdata, 0, rowsize);
|
||
|
pdata += plane_stride;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* use overlay to keep background transparent */
|
||
|
composite = self->overlay;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
GST_OBJECT_LOCK (vagg);
|
||
|
for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
|
||
|
GstVideoAggregatorPad *pad = l->data;
|
||
|
GstCompositorPad *compo_pad = GST_COMPOSITOR_PAD (pad);
|
||
|
|
||
|
if (pad->aggregated_frame != NULL) {
|
||
|
composite (pad->aggregated_frame, compo_pad->xpos, compo_pad->ypos,
|
||
|
compo_pad->alpha, outframe);
|
||
|
}
|
||
|
}
|
||
|
GST_OBJECT_UNLOCK (vagg);
|
||
|
|
||
|
gst_video_frame_unmap (outframe);
|
||
|
|
||
|
return GST_FLOW_OK;
|
||
|
}
|
||
|
|
||
|
/* GObject boilerplate */
|
||
|
static void
|
||
|
gst_compositor_class_init (GstCompositorClass * klass)
|
||
|
{
|
||
|
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||
|
GstElementClass *gstelement_class = (GstElementClass *) klass;
|
||
|
GstVideoAggregatorClass *videoaggregator_class =
|
||
|
(GstVideoAggregatorClass *) klass;
|
||
|
GstAggregatorClass *agg_class = (GstAggregatorClass *) klass;
|
||
|
|
||
|
gobject_class->get_property = gst_compositor_get_property;
|
||
|
gobject_class->set_property = gst_compositor_set_property;
|
||
|
|
||
|
agg_class->sinkpads_type = GST_TYPE_COMPOSITOR_PAD;
|
||
|
videoaggregator_class->update_info = _update_info;
|
||
|
videoaggregator_class->aggregate_frames = gst_compositor_aggregate_frames;
|
||
|
|
||
|
g_object_class_install_property (gobject_class, PROP_BACKGROUND,
|
||
|
g_param_spec_enum ("background", "Background", "Background type",
|
||
|
GST_TYPE_COMPOSITOR_BACKGROUND,
|
||
|
DEFAULT_BACKGROUND, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||
|
|
||
|
gst_element_class_add_pad_template (gstelement_class,
|
||
|
gst_static_pad_template_get (&src_factory));
|
||
|
gst_element_class_add_pad_template (gstelement_class,
|
||
|
gst_static_pad_template_get (&sink_factory));
|
||
|
|
||
|
gst_element_class_set_static_metadata (gstelement_class, "Compositor",
|
||
|
"Filter/Editor/Video/Compositor",
|
||
|
"Composite multiple video streams", "Wim Taymans <wim@fluendo.com>, "
|
||
|
"Sebastian Dröge <sebastian.droege@collabora.co.uk>");
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
gst_compositor_init (GstCompositor * self)
|
||
|
{
|
||
|
self->background = DEFAULT_BACKGROUND;
|
||
|
/* initialize variables */
|
||
|
}
|
||
|
|
||
|
/* Element registration */
|
||
|
static gboolean
|
||
|
plugin_init (GstPlugin * plugin)
|
||
|
{
|
||
|
GST_DEBUG_CATEGORY_INIT (gst_compositor_debug, "compositor", 0, "compositor");
|
||
|
|
||
|
gst_compositor_init_blend ();
|
||
|
|
||
|
return gst_element_register (plugin, "compositor", GST_RANK_PRIMARY + 1,
|
||
|
GST_TYPE_COMPOSITOR);
|
||
|
}
|
||
|
|
||
|
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
|
||
|
GST_VERSION_MINOR,
|
||
|
compositor,
|
||
|
"Compositor", plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME,
|
||
|
GST_PACKAGE_ORIGIN)
|