mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-18 14:26:43 +00:00
cairooverlay: Add property for drawing on a transparent surface and then blending
This allows us to use the GstVideoOverlayComposition API and correctly handle pre-multiplied alpha, while also only doing the alpha conversion once instead of twice for the whole frame. At a later point we can attach the meta to the buffer instead of blending ourselves if downstream supports that. https://bugzilla.gnome.org/show_bug.cgi?id=797091
This commit is contained in:
parent
defae35035
commit
9844bdbf7a
2 changed files with 118 additions and 5 deletions
|
@ -119,6 +119,14 @@ GST_STATIC_PAD_TEMPLATE ("sink",
|
||||||
|
|
||||||
G_DEFINE_TYPE (GstCairoOverlay, gst_cairo_overlay, GST_TYPE_VIDEO_FILTER);
|
G_DEFINE_TYPE (GstCairoOverlay, gst_cairo_overlay, GST_TYPE_VIDEO_FILTER);
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PROP_0,
|
||||||
|
PROP_DRAW_ON_TRANSPARENT_SURFACE,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DEFAULT_DRAW_ON_TRANSPARENT_SURFACE (FALSE)
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
SIGNAL_DRAW,
|
SIGNAL_DRAW,
|
||||||
|
@ -128,6 +136,46 @@ enum
|
||||||
|
|
||||||
static guint gst_cairo_overlay_signals[N_SIGNALS];
|
static guint gst_cairo_overlay_signals[N_SIGNALS];
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_cairo_overlay_set_property (GObject * object, guint property_id,
|
||||||
|
const GValue * value, GParamSpec * pspec)
|
||||||
|
{
|
||||||
|
GstCairoOverlay *overlay = GST_CAIRO_OVERLAY (object);
|
||||||
|
|
||||||
|
GST_OBJECT_LOCK (overlay);
|
||||||
|
|
||||||
|
switch (property_id) {
|
||||||
|
case PROP_DRAW_ON_TRANSPARENT_SURFACE:
|
||||||
|
overlay->draw_on_transparent_surface = g_value_get_boolean (value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_OBJECT_UNLOCK (overlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_cairo_overlay_get_property (GObject * object, guint property_id,
|
||||||
|
GValue * value, GParamSpec * pspec)
|
||||||
|
{
|
||||||
|
GstCairoOverlay *overlay = GST_CAIRO_OVERLAY (object);
|
||||||
|
|
||||||
|
GST_OBJECT_LOCK (overlay);
|
||||||
|
|
||||||
|
switch (property_id) {
|
||||||
|
case PROP_DRAW_ON_TRANSPARENT_SURFACE:
|
||||||
|
g_value_set_boolean (value, overlay->draw_on_transparent_surface);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_OBJECT_UNLOCK (overlay);
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_cairo_overlay_set_info (GstVideoFilter * vfilter, GstCaps * in_caps,
|
gst_cairo_overlay_set_info (GstVideoFilter * vfilter, GstCaps * in_caps,
|
||||||
GstVideoInfo * in_info, GstCaps * out_caps, GstVideoInfo * out_info)
|
GstVideoInfo * in_info, GstCaps * out_caps, GstVideoInfo * out_info)
|
||||||
|
@ -148,6 +196,7 @@ gst_cairo_overlay_transform_frame_ip (GstVideoFilter * vfilter,
|
||||||
cairo_surface_t *surface;
|
cairo_surface_t *surface;
|
||||||
cairo_t *cr;
|
cairo_t *cr;
|
||||||
cairo_format_t format;
|
cairo_format_t format;
|
||||||
|
gboolean draw_on_transparent_surface = overlay->draw_on_transparent_surface;
|
||||||
|
|
||||||
switch (GST_VIDEO_FRAME_FORMAT (frame)) {
|
switch (GST_VIDEO_FRAME_FORMAT (frame)) {
|
||||||
case GST_VIDEO_FORMAT_ARGB:
|
case GST_VIDEO_FORMAT_ARGB:
|
||||||
|
@ -169,10 +218,18 @@ gst_cairo_overlay_transform_frame_ip (GstVideoFilter * vfilter,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
surface =
|
if (draw_on_transparent_surface) {
|
||||||
cairo_image_surface_create_for_data (GST_VIDEO_FRAME_PLANE_DATA (frame,
|
surface =
|
||||||
0), format, GST_VIDEO_FRAME_WIDTH (frame),
|
cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
|
||||||
GST_VIDEO_FRAME_HEIGHT (frame), GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0));
|
GST_VIDEO_FRAME_WIDTH (frame), GST_VIDEO_FRAME_HEIGHT (frame));
|
||||||
|
} else {
|
||||||
|
/* FIXME: Need to pre-multiply the alpha in case of ARGB32 */
|
||||||
|
surface =
|
||||||
|
cairo_image_surface_create_for_data (GST_VIDEO_FRAME_PLANE_DATA (frame,
|
||||||
|
0), format, GST_VIDEO_FRAME_WIDTH (frame),
|
||||||
|
GST_VIDEO_FRAME_HEIGHT (frame), GST_VIDEO_FRAME_PLANE_STRIDE (frame,
|
||||||
|
0));
|
||||||
|
}
|
||||||
|
|
||||||
if (G_UNLIKELY (!surface))
|
if (G_UNLIKELY (!surface))
|
||||||
return GST_FLOW_ERROR;
|
return GST_FLOW_ERROR;
|
||||||
|
@ -188,7 +245,47 @@ gst_cairo_overlay_transform_frame_ip (GstVideoFilter * vfilter,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
cairo_destroy (cr);
|
cairo_destroy (cr);
|
||||||
cairo_surface_destroy (surface);
|
|
||||||
|
if (draw_on_transparent_surface) {
|
||||||
|
guint size;
|
||||||
|
GstBuffer *surface_buffer;
|
||||||
|
GstVideoOverlayRectangle *rect;
|
||||||
|
GstVideoOverlayComposition *composition;
|
||||||
|
gsize offset[GST_VIDEO_MAX_PLANES] = { 0, };
|
||||||
|
gint stride[GST_VIDEO_MAX_PLANES] = { 0, };
|
||||||
|
|
||||||
|
size =
|
||||||
|
cairo_image_surface_get_height (surface) *
|
||||||
|
cairo_image_surface_get_stride (surface);
|
||||||
|
stride[0] = cairo_image_surface_get_stride (surface);
|
||||||
|
|
||||||
|
/* Create a GstVideoOverlayComposition for blending, this handles
|
||||||
|
* pre-multiplied alpha correctly */
|
||||||
|
surface_buffer =
|
||||||
|
gst_buffer_new_wrapped_full (0, cairo_image_surface_get_data (surface),
|
||||||
|
size, 0, size, surface, (GDestroyNotify) cairo_surface_destroy);
|
||||||
|
gst_buffer_add_video_meta_full (surface_buffer, GST_VIDEO_FRAME_FLAG_NONE,
|
||||||
|
(G_BYTE_ORDER ==
|
||||||
|
G_LITTLE_ENDIAN ? GST_VIDEO_FORMAT_BGRA : GST_VIDEO_FORMAT_ARGB),
|
||||||
|
GST_VIDEO_FRAME_WIDTH (frame), GST_VIDEO_FRAME_HEIGHT (frame), 1,
|
||||||
|
offset, stride);
|
||||||
|
rect =
|
||||||
|
gst_video_overlay_rectangle_new_raw (surface_buffer, 0, 0,
|
||||||
|
GST_VIDEO_FRAME_WIDTH (frame), GST_VIDEO_FRAME_HEIGHT (frame),
|
||||||
|
GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA);
|
||||||
|
gst_buffer_unref (surface_buffer);
|
||||||
|
composition = gst_video_overlay_composition_new (rect);
|
||||||
|
gst_video_overlay_rectangle_unref (rect);
|
||||||
|
|
||||||
|
g_assert (gst_video_overlay_composition_blend (composition, frame));
|
||||||
|
|
||||||
|
gst_video_overlay_composition_unref (composition);
|
||||||
|
|
||||||
|
/* TODO: Put as meta on the buffer */
|
||||||
|
} else {
|
||||||
|
cairo_surface_destroy (surface);
|
||||||
|
/* FIXME: Need to un-premultiply the alpha in case of ARGB32 */
|
||||||
|
}
|
||||||
|
|
||||||
return GST_FLOW_OK;
|
return GST_FLOW_OK;
|
||||||
}
|
}
|
||||||
|
@ -198,13 +295,28 @@ gst_cairo_overlay_class_init (GstCairoOverlayClass * klass)
|
||||||
{
|
{
|
||||||
GstVideoFilterClass *vfilter_class;
|
GstVideoFilterClass *vfilter_class;
|
||||||
GstElementClass *element_class;
|
GstElementClass *element_class;
|
||||||
|
GObjectClass *gobject_class;
|
||||||
|
|
||||||
vfilter_class = (GstVideoFilterClass *) klass;
|
vfilter_class = (GstVideoFilterClass *) klass;
|
||||||
element_class = (GstElementClass *) klass;
|
element_class = (GstElementClass *) klass;
|
||||||
|
gobject_class = (GObjectClass *) klass;
|
||||||
|
|
||||||
vfilter_class->set_info = gst_cairo_overlay_set_info;
|
vfilter_class->set_info = gst_cairo_overlay_set_info;
|
||||||
vfilter_class->transform_frame_ip = gst_cairo_overlay_transform_frame_ip;
|
vfilter_class->transform_frame_ip = gst_cairo_overlay_transform_frame_ip;
|
||||||
|
|
||||||
|
gobject_class->set_property = gst_cairo_overlay_set_property;
|
||||||
|
gobject_class->get_property = gst_cairo_overlay_get_property;
|
||||||
|
|
||||||
|
g_object_class_install_property (gobject_class,
|
||||||
|
PROP_DRAW_ON_TRANSPARENT_SURFACE,
|
||||||
|
g_param_spec_boolean ("draw-on-transparent-surface",
|
||||||
|
"Draw on transparent surface",
|
||||||
|
"Let the draw signal work on a transparent surface "
|
||||||
|
"and blend the results with the video at a later time",
|
||||||
|
DEFAULT_DRAW_ON_TRANSPARENT_SURFACE,
|
||||||
|
GST_PARAM_CONTROLLABLE | GST_PARAM_MUTABLE_PLAYING | G_PARAM_READWRITE
|
||||||
|
| G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GstCairoOverlay::draw:
|
* GstCairoOverlay::draw:
|
||||||
* @overlay: Overlay element emitting the signal.
|
* @overlay: Overlay element emitting the signal.
|
||||||
|
|
|
@ -45,6 +45,7 @@ typedef struct _GstCairoOverlayClass GstCairoOverlayClass;
|
||||||
|
|
||||||
struct _GstCairoOverlay {
|
struct _GstCairoOverlay {
|
||||||
GstVideoFilter video_filter;
|
GstVideoFilter video_filter;
|
||||||
|
gboolean draw_on_transparent_surface;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstCairoOverlayClass {
|
struct _GstCairoOverlayClass {
|
||||||
|
|
Loading…
Reference in a new issue