From b76c0a0caf46f69f14316f7cfbad326e4a36cebe Mon Sep 17 00:00:00 2001 From: "U. Artie Eoff" Date: Thu, 14 Nov 2019 12:02:19 -0800 Subject: [PATCH] libs: add a vaapi blend class Support for the VA-API VPP blend functions. --- gst-libs/gst/vaapi/gstvaapiblend.c | 369 +++++++++++++++++++++++++++++++++++++ gst-libs/gst/vaapi/gstvaapiblend.h | 65 +++++++ gst-libs/gst/vaapi/meson.build | 2 + 3 files changed, 436 insertions(+) create mode 100644 gst-libs/gst/vaapi/gstvaapiblend.c create mode 100644 gst-libs/gst/vaapi/gstvaapiblend.h diff --git a/gst-libs/gst/vaapi/gstvaapiblend.c b/gst-libs/gst/vaapi/gstvaapiblend.c new file mode 100644 index 0000000..7cf56b0 --- /dev/null +++ b/gst-libs/gst/vaapi/gstvaapiblend.c @@ -0,0 +1,369 @@ +/* + * gstvaapiblend.c - Video processing blend + * + * Copyright (C) 2019 Intel Corporation + * Author: U. Artie Eoff + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#include "sysdeps.h" +#include "gstvaapicompat.h" +#include "gstvaapiblend.h" +#include "gstvaapiutils.h" +#include "gstvaapivalue.h" +#include "gstvaapidisplay_priv.h" +#include "gstvaapisurface_priv.h" + +struct _GstVaapiBlend +{ + GstObject parent_instance; + + GstVaapiDisplay *display; + + VAConfigID va_config; + VAContextID va_context; + + guint32 flags; +}; + +typedef struct _GstVaapiBlendClass GstVaapiBlendClass; +struct _GstVaapiBlendClass +{ + GstObjectClass parent_class; +}; + +GST_DEBUG_CATEGORY_STATIC (gst_debug_vaapi_blend); +#ifndef GST_DISABLE_GST_DEBUG +#define GST_CAT_DEFAULT gst_debug_vaapi_blend +#else +#define GST_CAT_DEFAULT NULL +#endif + +G_DEFINE_TYPE_WITH_CODE (GstVaapiBlend, gst_vaapi_blend, GST_TYPE_OBJECT, + GST_DEBUG_CATEGORY_INIT (gst_debug_vaapi_blend, "vaapiblend", 0, + "VA-API Blend")); + +enum +{ + PROP_DISPLAY = 1, +}; + +static void +gst_vaapi_blend_set_property (GObject * object, guint property_id, + const GValue * value, GParamSpec * pspec) +{ + GstVaapiBlend *const blend = GST_VAAPI_BLEND (object); + + switch (property_id) { + case PROP_DISPLAY:{ + GstVaapiDisplay *display = g_value_get_object (value);; + if (display) { + if (GST_VAAPI_DISPLAY_HAS_VPP (display)) { + blend->display = gst_object_ref (display); + } else { + GST_WARNING_OBJECT (blend, "GstVaapiDisplay doesn't support VPP"); + } + } + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +gst_vaapi_blend_get_property (GObject * object, guint property_id, + GValue * value, GParamSpec * pspec) +{ + GstVaapiBlend *const blend = GST_VAAPI_BLEND (object); + + switch (property_id) { + case PROP_DISPLAY: + g_value_set_object (value, blend->display); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +gst_vaapi_blend_finalize (GObject * object) +{ + GstVaapiBlend *const blend = GST_VAAPI_BLEND (object); + + GST_VAAPI_DISPLAY_LOCK (blend->display); + + if (blend->va_context != VA_INVALID_ID) { + vaDestroyContext (GST_VAAPI_DISPLAY_VADISPLAY (blend->display), + blend->va_context); + blend->va_context = VA_INVALID_ID; + } + + if (blend->va_config != VA_INVALID_ID) { + vaDestroyConfig (GST_VAAPI_DISPLAY_VADISPLAY (blend->display), + blend->va_config); + blend->va_config = VA_INVALID_ID; + } + + GST_VAAPI_DISPLAY_UNLOCK (blend->display); + + gst_vaapi_display_replace (&blend->display, NULL); + + G_OBJECT_CLASS (gst_vaapi_blend_parent_class)->finalize (object); +} + +static void +gst_vaapi_blend_class_init (GstVaapiBlendClass * klass) +{ + GObjectClass *const object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = gst_vaapi_blend_set_property; + object_class->get_property = gst_vaapi_blend_get_property; + object_class->finalize = gst_vaapi_blend_finalize; + + g_object_class_install_property (object_class, PROP_DISPLAY, + g_param_spec_object ("display", "Gst VA-API Display", + "The VA-API display object to use", GST_TYPE_VAAPI_DISPLAY, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME)); +} + +static void +gst_vaapi_blend_init (GstVaapiBlend * blend) +{ + blend->display = NULL; + blend->va_config = VA_INVALID_ID; + blend->va_context = VA_INVALID_ID; + blend->flags = 0; +} + +static gboolean +gst_vaapi_blend_initialize (GstVaapiBlend * blend) +{ + VAStatus status; + VAProcPipelineCaps pipeline_caps = { 0, }; + + if (!blend->display) + return FALSE; + + status = vaCreateConfig (GST_VAAPI_DISPLAY_VADISPLAY (blend->display), + VAProfileNone, VAEntrypointVideoProc, NULL, 0, &blend->va_config); + if (!vaapi_check_status (status, "vaCreateConfig() [VPP]")) + return FALSE; + + status = vaCreateContext (GST_VAAPI_DISPLAY_VADISPLAY (blend->display), + blend->va_config, 0, 0, 0, NULL, 0, &blend->va_context); + if (!vaapi_check_status (status, "vaCreateContext() [VPP]")) + return FALSE; + +#if VA_CHECK_VERSION(1,1,0) + status = + vaQueryVideoProcPipelineCaps (GST_VAAPI_DISPLAY_VADISPLAY + (blend->display), blend->va_context, NULL, 0, &pipeline_caps); + if (vaapi_check_status (status, "vaQueryVideoProcPipelineCaps()")) + blend->flags = pipeline_caps.blend_flags; +#endif + + if (!(blend->flags & VA_BLEND_GLOBAL_ALPHA)) { + GST_WARNING_OBJECT (blend, "VPP does not support global alpha blending"); + return FALSE; + } + + return TRUE; +} + +GstVaapiBlend * +gst_vaapi_blend_new (GstVaapiDisplay * display) +{ + GstVaapiBlend *blend = g_object_new (GST_TYPE_VAAPI_BLEND, + "display", display, NULL); + + if (!gst_vaapi_blend_initialize (blend)) { + gst_object_unref (blend); + blend = NULL; + } + + return blend; +} + +void +gst_vaapi_blend_replace (GstVaapiBlend ** old_blend_ptr, + GstVaapiBlend * new_blend) +{ + g_return_if_fail (old_blend_ptr != NULL); + + gst_object_replace ((GstObject **) old_blend_ptr, GST_OBJECT (new_blend)); +} + +/** + * gst_vaapi_blend_process_begin: + * @blend: a #GstVaapiBlend + * @surface: the #GstVaapiSurface output target + * + * Locks the VA display and prepares the VA processing pipeline for rendering. + * + * If this function fails, then the VA display is unlocked before returning. + * + * If this function succeeds, it must be paired with a call to + * #gst_vaapi_blend_process_end to ensure the VA processing pipeline is + * finalized and the VA display is unlocked. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ +gboolean +gst_vaapi_blend_process_begin (GstVaapiBlend * blend, GstVaapiSurface * surface) +{ + VAStatus va_status; + + g_return_val_if_fail (blend != NULL, FALSE); + g_return_val_if_fail (surface != NULL, FALSE); + + GST_VAAPI_DISPLAY_LOCK (blend->display); + + va_status = vaBeginPicture (GST_VAAPI_DISPLAY_VADISPLAY (blend->display), + blend->va_context, GST_VAAPI_OBJECT_ID (surface)); + + if (!vaapi_check_status (va_status, "vaBeginPicture()")) { + GST_VAAPI_DISPLAY_UNLOCK (blend->display); + return FALSE; + } + + return TRUE; +} + +/** + * gst_vaapi_blend_process_render: + * @blend: a #GstVaapiBlend + * @surface: the source #GstVaapiSurface to send to the VA processing pipeline + * for rendering. + * @crop_rect: the @surface crop extents to process for rendering. + * @target_rect: the extents for which the @surface is rendered within the + * output surface. + * @alpha: the alpha value in the range 0.0 to 1.0, inclusive, for blending + * with the output surface background or with previously rendered surfaces. + * + * Renders the @surface in the currently active VA processing pipeline. + * + * This function must only be called after a successful call to + * #gst_vaapi_blend_process_begin and before calling + * #gst_vaapi_process_end. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ +gboolean +gst_vaapi_blend_process_render (GstVaapiBlend * blend, + const GstVaapiSurface * surface, const GstVaapiRectangle * crop_rect, + const GstVaapiRectangle * target_rect, gdouble alpha) +{ + VADisplay va_display; + VAStatus va_status; + VAProcPipelineParameterBuffer *param = NULL; + VABufferID id = VA_INVALID_ID; + VARectangle src_rect = { 0, }, dst_rect = { + 0,}; +#if VA_CHECK_VERSION(1,1,0) + VABlendState blend_state; +#endif + + g_return_val_if_fail (blend != NULL, FALSE); + g_return_val_if_fail (surface != NULL, FALSE); + + va_display = GST_VAAPI_DISPLAY_VADISPLAY (blend->display); + + /* Build surface region (source) */ + src_rect.width = GST_VAAPI_SURFACE_WIDTH (surface); + src_rect.height = GST_VAAPI_SURFACE_HEIGHT (surface); + if (crop_rect) { + if ((crop_rect->x + crop_rect->width > src_rect.width) || + (crop_rect->y + crop_rect->height > src_rect.height)) + return FALSE; + + src_rect.x = crop_rect->x; + src_rect.y = crop_rect->y; + src_rect.width = crop_rect->width; + src_rect.height = crop_rect->height; + } + + /* Build output region (target) */ + dst_rect.width = src_rect.width; + dst_rect.height = src_rect.height; + if (target_rect) { + dst_rect.x = target_rect->x; + dst_rect.y = target_rect->y; + dst_rect.width = target_rect->width; + dst_rect.height = target_rect->height; + } + + if (!vaapi_create_buffer (va_display, blend->va_context, + VAProcPipelineParameterBufferType, sizeof (*param), NULL, &id, + (gpointer *) & param)) + return FALSE; + + memset (param, 0, sizeof (*param)); + + param->surface = GST_VAAPI_OBJECT_ID (surface); + param->surface_region = &src_rect; + param->output_region = &dst_rect; + param->output_background_color = 0xff000000; + +#if VA_CHECK_VERSION(1,1,0) + blend_state.flags = VA_BLEND_GLOBAL_ALPHA; + blend_state.global_alpha = alpha; + param->blend_state = &blend_state; +#endif + + vaapi_unmap_buffer (va_display, id, NULL); + + va_status = vaRenderPicture (va_display, blend->va_context, &id, 1); + if (!vaapi_check_status (va_status, "vaRenderPicture()")) { + vaapi_destroy_buffer (va_display, &id); + return FALSE; + } + + vaapi_destroy_buffer (va_display, &id); + + return TRUE; +} + +/** + * gst_vaapi_blend_process_end: + * @blend: a #GstVaapiBlend + * + * Finalizes all pending render operations in the active VA processing pipeline + * and unlocks the VA display. + * + * This function must always be paired with a call to + * #gst_vaapi_blend_process_begin to ensure the VA display gets unlocked. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ +gboolean +gst_vaapi_blend_process_end (GstVaapiBlend * blend) +{ + VAStatus va_status; + + g_return_val_if_fail (blend != NULL, FALSE); + + va_status = vaEndPicture (GST_VAAPI_DISPLAY_VADISPLAY (blend->display), + blend->va_context); + + GST_VAAPI_DISPLAY_UNLOCK (blend->display); + + if (!vaapi_check_status (va_status, "vaEndPicture()")) + return FALSE; + + return TRUE; +} diff --git a/gst-libs/gst/vaapi/gstvaapiblend.h b/gst-libs/gst/vaapi/gstvaapiblend.h new file mode 100644 index 0000000..60c30c8 --- /dev/null +++ b/gst-libs/gst/vaapi/gstvaapiblend.h @@ -0,0 +1,65 @@ +/* + * gstvaapiblend.h - Video processing blend + * + * Copyright (C) 2019 Intel Corporation + * Author: U. Artie Eoff + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#ifndef GST_VAAPI_BLEND_H +#define GST_VAAPI_BLEND_H + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_VAAPI_BLEND \ + (gst_vaapi_blend_get_type ()) +#define GST_VAAPI_BLEND(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_VAAPI_BLEND, GstVaapiBlend)) +#define GST_IS_VAAPI_BLEND(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_VAAPI_BLEND)) + +typedef struct _GstVaapiBlend GstVaapiBlend; + +GstVaapiBlend * +gst_vaapi_blend_new (GstVaapiDisplay * display); + +void +gst_vaapi_blend_replace (GstVaapiBlend ** old_blend_ptr, + GstVaapiBlend * new_blend); + +gboolean +gst_vaapi_blend_process_begin (GstVaapiBlend * blend, + GstVaapiSurface * surface); + +gboolean +gst_vaapi_blend_process_render (GstVaapiBlend * blend, + const GstVaapiSurface * surface, const GstVaapiRectangle * crop_rect, + const GstVaapiRectangle * target_rect, gdouble alpha); + +gboolean +gst_vaapi_blend_process_end (GstVaapiBlend * blend); + +GType +gst_vaapi_blend_get_type (void) G_GNUC_CONST; + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstVaapiBlend, gst_object_unref) + +G_END_DECLS + +#endif /* GST_VAAPI_FILTER_H */ diff --git a/gst-libs/gst/vaapi/meson.build b/gst-libs/gst/vaapi/meson.build index 68b60eb..83f4a1d 100644 --- a/gst-libs/gst/vaapi/meson.build +++ b/gst-libs/gst/vaapi/meson.build @@ -1,4 +1,5 @@ gstlibvaapi_sources = [ + 'gstvaapiblend.c', 'gstvaapibufferproxy.c', 'gstvaapicodec_objects.c', 'gstvaapicontext.c', @@ -44,6 +45,7 @@ gstlibvaapi_sources = [ ] gstlibvaapi_headers = [ + 'gstvaapiblend.h', 'gstvaapibufferproxy.h', 'gstvaapidecoder.h', 'gstvaapidecoder_h264.h', -- 2.7.4