mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-09-09 05:38:58 +00:00
Initially GstVaSample processed its GstBuffer member to get the VASurfaceID. But it might cases where we already have the VASurfaceID to process by the filter. This patch enables the possibility to pass the surfaces rather than the buffers. In order to validate the surfaces a function to check surfaces were added. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1023>
1697 lines
47 KiB
C
1697 lines
47 KiB
C
/* GStreamer
|
|
* Copyright (C) 2020 Igalia, S.L.
|
|
* Author: Víctor Jáquez <vjaquez@igalia.com>
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "gstvafilter.h"
|
|
|
|
#include <gst/video/video.h>
|
|
|
|
#include <va/va_drmcommon.h>
|
|
|
|
#include "gstvaallocator.h"
|
|
#include "gstvacaps.h"
|
|
#include "gstvadisplay_priv.h"
|
|
#include "gstvavideoformat.h"
|
|
#include "vasurfaceimage.h"
|
|
|
|
struct _GstVaFilter
|
|
{
|
|
GstObject parent;
|
|
|
|
GstVaDisplay *display;
|
|
VAConfigID config;
|
|
VAContextID context;
|
|
|
|
/* hardware constraints */
|
|
VAProcPipelineCaps pipeline_caps;
|
|
|
|
guint32 mem_types;
|
|
gint min_width;
|
|
gint max_width;
|
|
gint min_height;
|
|
gint max_height;
|
|
|
|
GArray *surface_formats;
|
|
GArray *image_formats;
|
|
|
|
GArray *available_filters;
|
|
|
|
/* stream information */
|
|
guint mirror;
|
|
guint rotation;
|
|
GstVideoOrientationMethod orientation;
|
|
|
|
gboolean crop_enabled;
|
|
|
|
VARectangle input_region;
|
|
VARectangle output_region;
|
|
|
|
VAProcColorStandardType input_color_standard;
|
|
VAProcColorProperties input_color_properties;
|
|
VAProcColorStandardType output_color_standard;
|
|
VAProcColorProperties output_color_properties;
|
|
|
|
GArray *filters;
|
|
};
|
|
|
|
GST_DEBUG_CATEGORY_STATIC (gst_va_filter_debug);
|
|
#define GST_CAT_DEFAULT gst_va_filter_debug
|
|
|
|
#define gst_va_filter_parent_class parent_class
|
|
G_DEFINE_TYPE_WITH_CODE (GstVaFilter, gst_va_filter, GST_TYPE_OBJECT,
|
|
GST_DEBUG_CATEGORY_INIT (gst_va_filter_debug, "vafilter", 0, "VA Filter"));
|
|
|
|
enum
|
|
{
|
|
PROP_DISPLAY = 1,
|
|
N_PROPERTIES
|
|
};
|
|
|
|
static GParamSpec *g_properties[N_PROPERTIES];
|
|
|
|
static void
|
|
gst_va_filter_set_property (GObject * object, guint prop_id,
|
|
const GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstVaFilter *self = GST_VA_FILTER (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_DISPLAY:{
|
|
g_assert (!self->display);
|
|
self->display = g_value_dup_object (value);
|
|
break;
|
|
}
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_va_filter_get_property (GObject * object, guint prop_id, GValue * value,
|
|
GParamSpec * pspec)
|
|
{
|
|
GstVaFilter *self = GST_VA_FILTER (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_DISPLAY:
|
|
g_value_set_object (value, self->display);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_va_filter_dispose (GObject * object)
|
|
{
|
|
GstVaFilter *self = GST_VA_FILTER (object);
|
|
|
|
gst_va_filter_close (self);
|
|
|
|
g_clear_pointer (&self->available_filters, g_array_unref);
|
|
g_clear_pointer (&self->image_formats, g_array_unref);
|
|
g_clear_pointer (&self->surface_formats, g_array_unref);
|
|
gst_clear_object (&self->display);
|
|
|
|
G_OBJECT_CLASS (parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
gst_va_filter_class_init (GstVaFilterClass * klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
gobject_class->set_property = gst_va_filter_set_property;
|
|
gobject_class->get_property = gst_va_filter_get_property;
|
|
gobject_class->dispose = gst_va_filter_dispose;
|
|
|
|
g_properties[PROP_DISPLAY] =
|
|
g_param_spec_object ("display", "GstVaDisplay", "GstVADisplay object",
|
|
GST_TYPE_VA_DISPLAY,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
|
|
|
g_object_class_install_properties (gobject_class, N_PROPERTIES, g_properties);
|
|
}
|
|
|
|
static void
|
|
gst_va_filter_init (GstVaFilter * self)
|
|
{
|
|
self->config = VA_INVALID_ID;
|
|
self->context = VA_INVALID_ID;
|
|
|
|
self->min_height = 1;
|
|
self->max_height = G_MAXINT;
|
|
self->min_width = 1;
|
|
self->max_width = G_MAXINT;
|
|
}
|
|
|
|
GstVaFilter *
|
|
gst_va_filter_new (GstVaDisplay * display)
|
|
{
|
|
g_return_val_if_fail (GST_IS_VA_DISPLAY (display), NULL);
|
|
|
|
return g_object_new (GST_TYPE_VA_FILTER, "display", display, NULL);
|
|
}
|
|
|
|
gboolean
|
|
gst_va_filter_is_open (GstVaFilter * self)
|
|
{
|
|
gboolean ret;
|
|
|
|
g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
|
|
|
|
GST_OBJECT_LOCK (self);
|
|
ret = (self->config != VA_INVALID_ID && self->context != VA_INVALID_ID);
|
|
GST_OBJECT_UNLOCK (self);
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
gst_va_filter_ensure_config_attributes (GstVaFilter * self,
|
|
guint32 * rt_formats_ptr)
|
|
{
|
|
VAConfigAttrib attribs[] = {
|
|
{.type = VAConfigAttribMaxPictureWidth,},
|
|
{.type = VAConfigAttribMaxPictureHeight,},
|
|
{.type = VAConfigAttribRTFormat,},
|
|
};
|
|
VADisplay dpy;
|
|
VAStatus status;
|
|
guint i, value, rt_formats = 0, max_width = 0, max_height = 0;
|
|
|
|
dpy = gst_va_display_get_va_dpy (self->display);
|
|
|
|
gst_va_display_lock (self->display);
|
|
status = vaGetConfigAttributes (dpy, VAProfileNone, VAEntrypointVideoProc,
|
|
attribs, G_N_ELEMENTS (attribs));
|
|
gst_va_display_unlock (self->display);
|
|
if (status != VA_STATUS_SUCCESS) {
|
|
GST_ERROR_OBJECT (self, "vaGetConfigAttributes: %s", vaErrorStr (status));
|
|
return FALSE;
|
|
}
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (attribs); i++) {
|
|
value = attribs[i].value;
|
|
if (value == VA_ATTRIB_NOT_SUPPORTED)
|
|
continue;
|
|
switch (attribs[i].type) {
|
|
case VAConfigAttribMaxPictureHeight:
|
|
max_height = value;
|
|
break;
|
|
case VAConfigAttribMaxPictureWidth:
|
|
max_width = value;
|
|
break;
|
|
case VAConfigAttribRTFormat:
|
|
rt_formats = value;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (rt_formats_ptr && rt_formats != 0)
|
|
*rt_formats_ptr = rt_formats;
|
|
if (max_width > 0 && max_width < G_MAXINT)
|
|
self->max_width = max_width;
|
|
if (max_height > 0 && max_height < G_MAXINT)
|
|
self->max_height = max_height;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* There are formats that are not handled correctly by driver */
|
|
static gboolean
|
|
format_is_accepted (GstVaFilter * self, GstVideoFormat format)
|
|
{
|
|
/* https://github.com/intel/media-driver/issues/690
|
|
* https://github.com/intel/media-driver/issues/644 */
|
|
if (!gst_va_display_is_implementation (self->display,
|
|
GST_VA_IMPLEMENTATION_INTEL_IHD))
|
|
return TRUE;
|
|
|
|
switch (format) {
|
|
case GST_VIDEO_FORMAT_ARGB:
|
|
case GST_VIDEO_FORMAT_xRGB:
|
|
case GST_VIDEO_FORMAT_ABGR:
|
|
case GST_VIDEO_FORMAT_xBGR:
|
|
return FALSE;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_va_filter_ensure_surface_attributes (GstVaFilter * self)
|
|
{
|
|
GArray *surface_formats;
|
|
GstVideoFormat format;
|
|
VASurfaceAttrib *attribs;
|
|
guint i, attrib_count;
|
|
|
|
attribs =
|
|
gst_va_get_surface_attribs (self->display, self->config, &attrib_count);
|
|
if (!attribs)
|
|
return FALSE;
|
|
surface_formats = g_array_new (FALSE, FALSE, sizeof (GstVideoFormat));
|
|
|
|
for (i = 0; i < attrib_count; i++) {
|
|
if (attribs[i].value.type != VAGenericValueTypeInteger)
|
|
continue;
|
|
switch (attribs[i].type) {
|
|
case VASurfaceAttribPixelFormat:
|
|
format = gst_va_video_format_from_va_fourcc (attribs[i].value.value.i);
|
|
if (format != GST_VIDEO_FORMAT_UNKNOWN
|
|
&& format_is_accepted (self, format))
|
|
g_array_append_val (surface_formats, format);
|
|
break;
|
|
case VASurfaceAttribMinWidth:
|
|
self->min_width = MAX (self->min_width, attribs[i].value.value.i);
|
|
break;
|
|
case VASurfaceAttribMaxWidth:
|
|
if (self->max_width > 0)
|
|
self->max_width = MIN (self->max_width, attribs[i].value.value.i);
|
|
else
|
|
self->max_width = attribs[i].value.value.i;
|
|
break;
|
|
case VASurfaceAttribMinHeight:
|
|
self->min_height = MAX (self->min_height, attribs[i].value.value.i);
|
|
break;
|
|
case VASurfaceAttribMaxHeight:
|
|
if (self->max_height > 0)
|
|
self->max_height = MIN (self->max_height, attribs[i].value.value.i);
|
|
else
|
|
self->max_height = attribs[i].value.value.i;
|
|
break;
|
|
case VASurfaceAttribMemoryType:
|
|
self->mem_types = attribs[i].value.value.i;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (surface_formats->len == 0)
|
|
g_clear_pointer (&surface_formats, g_array_unref);
|
|
|
|
self->surface_formats = surface_formats;
|
|
|
|
g_free (attribs);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_va_filter_ensure_pipeline_caps (GstVaFilter * self)
|
|
{
|
|
VADisplay dpy;
|
|
VAStatus status;
|
|
|
|
dpy = gst_va_display_get_va_dpy (self->display);
|
|
|
|
gst_va_display_lock (self->display);
|
|
status = vaQueryVideoProcPipelineCaps (dpy, self->context, NULL, 0,
|
|
&self->pipeline_caps);
|
|
gst_va_display_unlock (self->display);
|
|
if (status != VA_STATUS_SUCCESS) {
|
|
GST_ERROR_OBJECT (self, "vaQueryVideoProcPipelineCaps: %s",
|
|
vaErrorStr (status));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* Not thread-safe API */
|
|
gboolean
|
|
gst_va_filter_open (GstVaFilter * self)
|
|
{
|
|
VAConfigAttrib attrib = {
|
|
.type = VAConfigAttribRTFormat,
|
|
};
|
|
VADisplay dpy;
|
|
VAStatus status;
|
|
|
|
g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
|
|
|
|
if (gst_va_filter_is_open (self))
|
|
return TRUE;
|
|
|
|
if (!gst_va_filter_ensure_config_attributes (self, &attrib.value))
|
|
return FALSE;
|
|
|
|
if (!gst_va_filter_ensure_pipeline_caps (self))
|
|
return FALSE;
|
|
|
|
self->image_formats = gst_va_display_get_image_formats (self->display);
|
|
if (!self->image_formats)
|
|
return FALSE;
|
|
|
|
dpy = gst_va_display_get_va_dpy (self->display);
|
|
|
|
gst_va_display_lock (self->display);
|
|
status = vaCreateConfig (dpy, VAProfileNone, VAEntrypointVideoProc, &attrib,
|
|
1, &self->config);
|
|
gst_va_display_unlock (self->display);
|
|
if (status != VA_STATUS_SUCCESS) {
|
|
GST_ERROR_OBJECT (self, "vaCreateConfig: %s", vaErrorStr (status));
|
|
return FALSE;
|
|
}
|
|
|
|
if (!gst_va_filter_ensure_surface_attributes (self))
|
|
goto bail;
|
|
|
|
gst_va_display_lock (self->display);
|
|
status = vaCreateContext (dpy, self->config, 0, 0, 0, NULL, 0,
|
|
&self->context);
|
|
gst_va_display_unlock (self->display);
|
|
if (status != VA_STATUS_SUCCESS) {
|
|
GST_ERROR_OBJECT (self, "vaCreateContext: %s", vaErrorStr (status));
|
|
goto bail;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
bail:
|
|
{
|
|
gst_va_display_lock (self->display);
|
|
status = vaDestroyConfig (dpy, self->config);
|
|
gst_va_display_unlock (self->display);
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Not thread-safe API */
|
|
gboolean
|
|
gst_va_filter_close (GstVaFilter * self)
|
|
{
|
|
VADisplay dpy;
|
|
VAStatus status;
|
|
|
|
g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
|
|
|
|
if (!gst_va_filter_is_open (self))
|
|
return TRUE;
|
|
|
|
dpy = gst_va_display_get_va_dpy (self->display);
|
|
|
|
if (self->context != VA_INVALID_ID) {
|
|
gst_va_display_lock (self->display);
|
|
status = vaDestroyContext (dpy, self->context);
|
|
gst_va_display_unlock (self->display);
|
|
if (status != VA_STATUS_SUCCESS)
|
|
GST_ERROR_OBJECT (self, "vaDestroyContext: %s", vaErrorStr (status));
|
|
}
|
|
|
|
gst_va_display_lock (self->display);
|
|
status = vaDestroyConfig (dpy, self->config);
|
|
gst_va_display_unlock (self->display);
|
|
if (status != VA_STATUS_SUCCESS) {
|
|
GST_ERROR_OBJECT (self, "vaDestroyConfig: %s", vaErrorStr (status));
|
|
return FALSE;
|
|
}
|
|
|
|
g_clear_pointer (&self->available_filters, g_array_unref);
|
|
g_clear_pointer (&self->filters, g_array_unref);
|
|
|
|
gst_va_filter_init (self);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* *INDENT-OFF* */
|
|
static const struct VaFilterCapMap {
|
|
VAProcFilterType type;
|
|
guint count;
|
|
} filter_cap_map[] = {
|
|
{ VAProcFilterNoiseReduction, 1 },
|
|
{ VAProcFilterDeinterlacing, VAProcDeinterlacingCount },
|
|
{ VAProcFilterSharpening, 1 },
|
|
{ VAProcFilterColorBalance, VAProcColorBalanceCount },
|
|
{ VAProcFilterSkinToneEnhancement, 1 },
|
|
{ VAProcFilterTotalColorCorrection, VAProcTotalColorCorrectionCount },
|
|
{ VAProcFilterHVSNoiseReduction, 0 },
|
|
{ VAProcFilterHighDynamicRangeToneMapping, 1 },
|
|
};
|
|
/* *INDENT-ON* */
|
|
|
|
static const struct VaFilterCapMap *
|
|
gst_va_filter_get_filter_cap (VAProcFilterType type)
|
|
{
|
|
guint i;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (filter_cap_map); i++) {
|
|
if (filter_cap_map[i].type == type)
|
|
return &filter_cap_map[i];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static guint
|
|
gst_va_filter_get_filter_cap_count (VAProcFilterType type)
|
|
{
|
|
const struct VaFilterCapMap *map = gst_va_filter_get_filter_cap (type);
|
|
return map ? map->count : 0;
|
|
}
|
|
|
|
struct VaFilter
|
|
{
|
|
VAProcFilterType type;
|
|
guint num_caps;
|
|
union
|
|
{
|
|
VAProcFilterCap simple;
|
|
VAProcFilterCapDeinterlacing deint[VAProcDeinterlacingCount];
|
|
VAProcFilterCapColorBalance cb[VAProcColorBalanceCount];
|
|
VAProcFilterCapTotalColorCorrection cc[VAProcTotalColorCorrectionCount];
|
|
VAProcFilterCapHighDynamicRange hdr;
|
|
} caps;
|
|
};
|
|
|
|
static gboolean
|
|
gst_va_filter_ensure_filters (GstVaFilter * self)
|
|
{
|
|
GArray *filters;
|
|
VADisplay dpy;
|
|
VAProcFilterType *filter_types;
|
|
VAStatus status;
|
|
guint i, num = VAProcFilterCount;
|
|
gboolean ret = FALSE;
|
|
|
|
GST_OBJECT_LOCK (self);
|
|
if (self->available_filters) {
|
|
GST_OBJECT_UNLOCK (self);
|
|
return TRUE;
|
|
}
|
|
GST_OBJECT_UNLOCK (self);
|
|
|
|
filter_types = g_malloc_n (num, sizeof (*filter_types));
|
|
|
|
dpy = gst_va_display_get_va_dpy (self->display);
|
|
|
|
gst_va_display_lock (self->display);
|
|
status = vaQueryVideoProcFilters (dpy, self->context, filter_types, &num);
|
|
gst_va_display_unlock (self->display);
|
|
if (status == VA_STATUS_ERROR_MAX_NUM_EXCEEDED) {
|
|
filter_types = g_try_realloc_n (filter_types, num, sizeof (*filter_types));
|
|
gst_va_display_lock (self->display);
|
|
status = vaQueryVideoProcFilters (dpy, self->context, filter_types, &num);
|
|
gst_va_display_unlock (self->display);
|
|
}
|
|
if (status != VA_STATUS_SUCCESS) {
|
|
GST_ERROR_OBJECT (self, "vaQueryVideoProcFilters: %s", vaErrorStr (status));
|
|
goto bail;
|
|
}
|
|
|
|
if (num == 0)
|
|
goto bail;
|
|
|
|
filters = g_array_sized_new (FALSE, FALSE, sizeof (struct VaFilter), num);
|
|
|
|
for (i = 0; i < num; i++) {
|
|
guint num_caps = gst_va_filter_get_filter_cap_count (filter_types[i]);
|
|
struct VaFilter filter = { filter_types[i], num_caps, {{{0,}}} };
|
|
|
|
if (num_caps > 0) {
|
|
gst_va_display_lock (self->display);
|
|
status = vaQueryVideoProcFilterCaps (dpy, self->context, filter.type,
|
|
&filter.caps, &filter.num_caps);
|
|
gst_va_display_unlock (self->display);
|
|
if (status != VA_STATUS_SUCCESS) {
|
|
GST_WARNING_OBJECT (self, "vaQueryVideoProcFiltersCaps: %s",
|
|
vaErrorStr (status));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
g_array_append_val (filters, filter);
|
|
}
|
|
|
|
GST_OBJECT_LOCK (self);
|
|
g_clear_pointer (&self->available_filters, g_array_unref);
|
|
self->available_filters = filters;
|
|
GST_OBJECT_UNLOCK (self);
|
|
|
|
ret = TRUE;
|
|
|
|
bail:
|
|
g_free (filter_types);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* *INDENT-OFF* */
|
|
static const struct _CBDesc {
|
|
const char *name;
|
|
const char *nick;
|
|
const char *blurb;
|
|
guint prop_id;
|
|
} cb_desc[VAProcColorBalanceCount] = {
|
|
[VAProcColorBalanceHue] =
|
|
{ "hue", "Hue", "Color hue value", GST_VA_FILTER_PROP_HUE },
|
|
[VAProcColorBalanceSaturation] =
|
|
{ "saturation", "Saturation", "Color saturation value",
|
|
GST_VA_FILTER_PROP_SATURATION },
|
|
[VAProcColorBalanceBrightness] =
|
|
{ "brightness", "Brightness", "Color brightness value",
|
|
GST_VA_FILTER_PROP_BRIGHTNESS },
|
|
[VAProcColorBalanceContrast] =
|
|
{ "contrast", "Contrast", "Color contrast value",
|
|
GST_VA_FILTER_PROP_CONTRAST },
|
|
[VAProcColorBalanceAutoSaturation] =
|
|
{ "auto-saturation", "Auto-Saturation", "Enable auto saturation",
|
|
GST_VA_FILTER_PROP_AUTO_SATURATION },
|
|
[VAProcColorBalanceAutoBrightness] =
|
|
{ "auto-brightness", "Auto-Brightness", "Enable auto brightness",
|
|
GST_VA_FILTER_PROP_AUTO_BRIGHTNESS },
|
|
[VAProcColorBalanceAutoContrast] =
|
|
{ "auto-contrast", "Auto-Contrast", "Enable auto contrast",
|
|
GST_VA_FILTER_PROP_AUTO_CONTRAST },
|
|
};
|
|
/* *INDENT-ON* */
|
|
|
|
gboolean
|
|
gst_va_filter_install_properties (GstVaFilter * self, GObjectClass * klass)
|
|
{
|
|
guint i;
|
|
const GParamFlags common_flags = G_PARAM_READWRITE
|
|
| GST_PARAM_CONDITIONALLY_AVAILABLE | G_PARAM_STATIC_STRINGS
|
|
| GST_PARAM_MUTABLE_PLAYING | GST_PARAM_CONTROLLABLE;
|
|
|
|
g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
|
|
|
|
if (!gst_va_filter_is_open (self))
|
|
return FALSE;
|
|
|
|
if (!gst_va_filter_ensure_filters (self))
|
|
return FALSE;
|
|
|
|
for (i = 0; i < self->available_filters->len; i++) {
|
|
const struct VaFilter *filter =
|
|
&g_array_index (self->available_filters, struct VaFilter, i);
|
|
|
|
switch (filter->type) {
|
|
case VAProcFilterNoiseReduction:{
|
|
const VAProcFilterCap *caps = &filter->caps.simple;
|
|
|
|
g_object_class_install_property (klass, GST_VA_FILTER_PROP_DENOISE,
|
|
g_param_spec_float ("denoise", "Noise reduction",
|
|
"Noise reduction factor", caps->range.min_value,
|
|
caps->range.max_value, caps->range.default_value,
|
|
common_flags));
|
|
break;
|
|
}
|
|
case VAProcFilterSharpening:{
|
|
const VAProcFilterCap *caps = &filter->caps.simple;
|
|
|
|
g_object_class_install_property (klass, GST_VA_FILTER_PROP_SHARPEN,
|
|
g_param_spec_float ("sharpen", "Sharpening Level",
|
|
"Sharpening/blurring filter", caps->range.min_value,
|
|
caps->range.max_value, caps->range.default_value,
|
|
common_flags));
|
|
break;
|
|
}
|
|
case VAProcFilterSkinToneEnhancement:{
|
|
const VAProcFilterCap *caps = &filter->caps.simple;
|
|
GParamSpec *pspec;
|
|
|
|
/* i965 filter */
|
|
if (filter->num_caps == 0) {
|
|
pspec = g_param_spec_boolean ("skin-tone", "Skin Tone Enhancenment",
|
|
"Skin Tone Enhancenment filter", FALSE, common_flags);
|
|
} else {
|
|
pspec = g_param_spec_float ("skin-tone", "Skin Tone Enhancenment",
|
|
"Skin Tone Enhancenment filter", caps->range.min_value,
|
|
caps->range.max_value, caps->range.default_value, common_flags);
|
|
}
|
|
|
|
g_object_class_install_property (klass, GST_VA_FILTER_PROP_SKINTONE,
|
|
pspec);
|
|
break;
|
|
}
|
|
case VAProcFilterColorBalance:{
|
|
const VAProcFilterCapColorBalance *caps = filter->caps.cb;
|
|
GParamSpec *pspec;
|
|
guint j, k;
|
|
|
|
for (j = 0; j < filter->num_caps; j++) {
|
|
k = caps[j].type;
|
|
if (caps[j].range.min_value < caps[j].range.max_value) {
|
|
pspec = g_param_spec_float (cb_desc[k].name, cb_desc[k].nick,
|
|
cb_desc[k].blurb, caps[j].range.min_value,
|
|
caps[j].range.max_value, caps[j].range.default_value,
|
|
common_flags);
|
|
} else {
|
|
pspec = g_param_spec_boolean (cb_desc[k].name, cb_desc[k].nick,
|
|
cb_desc[k].blurb, FALSE, common_flags);
|
|
}
|
|
|
|
g_object_class_install_property (klass, cb_desc[k].prop_id, pspec);
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (self->pipeline_caps.mirror_flags != VA_MIRROR_NONE
|
|
|| self->pipeline_caps.rotation_flags != VA_ROTATION_NONE) {
|
|
g_object_class_install_property (klass, GST_VA_FILTER_PROP_VIDEO_DIR,
|
|
g_param_spec_enum ("video-direction", "Video Direction",
|
|
"Video direction: rotation and flipping",
|
|
GST_TYPE_VIDEO_ORIENTATION_METHOD, GST_VIDEO_ORIENTATION_IDENTITY,
|
|
common_flags));
|
|
}
|
|
|
|
/**
|
|
* GstVaPostProc:disable-passthrough:
|
|
*
|
|
* If set to %TRUE the filter will not enable passthrough mode, thus
|
|
* each frame will be processed. It's useful for cropping, for
|
|
* example.
|
|
*
|
|
* Since: 1.20
|
|
*/
|
|
g_object_class_install_property (klass,
|
|
GST_VA_FILTER_PROP_DISABLE_PASSTHROUGH,
|
|
g_param_spec_boolean ("disable-passthrough", "Disable Passthrough",
|
|
"Forces passing buffers through the postprocessor", FALSE,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS
|
|
| GST_PARAM_MUTABLE_READY));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* GstVaDeinterlaceMethods:
|
|
* @GST_VA_DEINTERLACE_BOB: Interpolating missing lines by using the
|
|
* adjacent lines.
|
|
* @GST_VA_DEINTERLACE_WEAVE: Show both fields per frame. (don't use)
|
|
* @GST_VA_DEINTERLACE_ADAPTIVE: Interpolating missing lines by using
|
|
* spatial/temporal references.
|
|
* @GST_VA_DEINTERLACE_COMPENSATED: Recreating missing lines by using
|
|
* motion vector.
|
|
*
|
|
* Since: 1.20
|
|
*/
|
|
/* *INDENT-OFF* */
|
|
static const GEnumValue di_desc[] = {
|
|
[GST_VA_DEINTERLACE_BOB] =
|
|
{ VAProcDeinterlacingBob,
|
|
"Bob: Interpolating missing lines by using the adjacent lines.", "bob" },
|
|
[GST_VA_DEINTERLACE_WEAVE] =
|
|
{ VAProcDeinterlacingWeave, "Weave: Show both fields per frame. (don't use)",
|
|
"weave" },
|
|
[GST_VA_DEINTERLACE_ADAPTIVE] =
|
|
{ VAProcDeinterlacingMotionAdaptive,
|
|
"Adaptive: Interpolating missing lines by using spatial/temporal references.",
|
|
"adaptive" },
|
|
[GST_VA_DEINTERLACE_COMPENSATED] =
|
|
{ VAProcDeinterlacingMotionCompensated,
|
|
"Compensation: Recreating missing lines by using motion vector.",
|
|
"compensated" },
|
|
};
|
|
/* *INDENT-ON* */
|
|
|
|
static GType
|
|
gst_va_deinterlace_methods_get_type (guint num_caps,
|
|
const VAProcFilterCapDeinterlacing * caps)
|
|
{
|
|
guint i, j = 0;
|
|
static GType deinterlace_methods_type = 0;
|
|
static GEnumValue methods_types[VAProcDeinterlacingCount];
|
|
|
|
if (deinterlace_methods_type > 0)
|
|
return deinterlace_methods_type;
|
|
|
|
for (i = 0; i < num_caps; i++) {
|
|
if (caps[i].type > VAProcDeinterlacingNone
|
|
&& caps[i].type < VAProcDeinterlacingCount)
|
|
methods_types[j++] = di_desc[caps[i].type];
|
|
}
|
|
|
|
/* *INDENT-OFF* */
|
|
methods_types[j] = (GEnumValue) { 0, NULL, NULL };
|
|
/* *INDENT-ON* */
|
|
|
|
deinterlace_methods_type = g_enum_register_static ("GstVaDeinterlaceMethods",
|
|
(const GEnumValue *) methods_types);
|
|
|
|
return deinterlace_methods_type;
|
|
}
|
|
|
|
gboolean
|
|
gst_va_filter_install_deinterlace_properties (GstVaFilter * self,
|
|
GObjectClass * klass)
|
|
{
|
|
GType type;
|
|
guint i;
|
|
const GParamFlags common_flags = G_PARAM_READWRITE
|
|
| G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_PLAYING;
|
|
|
|
g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
|
|
|
|
if (!gst_va_filter_is_open (self))
|
|
return FALSE;
|
|
|
|
if (!gst_va_filter_ensure_filters (self))
|
|
return FALSE;
|
|
|
|
for (i = 0; i < self->available_filters->len; i++) {
|
|
const struct VaFilter *filter =
|
|
&g_array_index (self->available_filters, struct VaFilter, i);
|
|
|
|
if (filter->type == VAProcFilterDeinterlacing) {
|
|
guint i, default_method = 0;
|
|
const VAProcFilterCapDeinterlacing *caps = filter->caps.deint;
|
|
if (!caps)
|
|
break;
|
|
|
|
/* use the first method in the list as default */
|
|
for (i = 0; i < filter->num_caps; i++) {
|
|
if (caps[i].type > VAProcDeinterlacingNone
|
|
&& caps[i].type < VAProcDeinterlacingCount) {
|
|
default_method = caps[i].type;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (default_method == 0)
|
|
break;
|
|
|
|
type = gst_va_deinterlace_methods_get_type (filter->num_caps, caps);
|
|
gst_type_mark_as_plugin_api (type, 0);
|
|
|
|
/**
|
|
* GstVaDeinterlace:method
|
|
*
|
|
* Selects the different deinterlacing algorithms that can be used.
|
|
*
|
|
* It depends on the driver the number of available algorithms,
|
|
* and they provide different quality and different processing
|
|
* consumption.
|
|
*
|
|
* Since: 1.20
|
|
*/
|
|
g_object_class_install_property (klass,
|
|
GST_VA_FILTER_PROP_DEINTERLACE_METHOD,
|
|
g_param_spec_enum ("method", "Method", "Deinterlace Method",
|
|
type, default_method, common_flags));
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
gst_va_filter_has_filter (GstVaFilter * self, VAProcFilterType type)
|
|
{
|
|
guint i;
|
|
|
|
g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
|
|
|
|
if (!gst_va_filter_is_open (self))
|
|
return FALSE;
|
|
|
|
if (!gst_va_filter_ensure_filters (self))
|
|
return FALSE;
|
|
|
|
for (i = 0; i < self->available_filters->len; i++) {
|
|
const struct VaFilter *filter =
|
|
&g_array_index (self->available_filters, struct VaFilter, i);
|
|
|
|
if (filter->type == type)
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
const gpointer
|
|
gst_va_filter_get_filter_caps (GstVaFilter * self, VAProcFilterType type,
|
|
guint * num_caps)
|
|
{
|
|
struct VaFilter *filter = NULL;
|
|
/* *INDENT-OFF* */
|
|
static const VAProcFilterCap i965_ste_caps = {
|
|
.range = {
|
|
.min_value = 0.0,
|
|
.max_value = 1.0,
|
|
.default_value = 0.0,
|
|
.step = 1.0,
|
|
},
|
|
};
|
|
/* *INDENT-ON* */
|
|
gpointer ret = NULL;
|
|
guint i;
|
|
|
|
if (!gst_va_filter_is_open (self))
|
|
return FALSE;
|
|
|
|
if (!gst_va_filter_ensure_filters (self))
|
|
return FALSE;
|
|
|
|
GST_OBJECT_LOCK (self);
|
|
for (i = 0; i < self->available_filters->len; i++) {
|
|
filter = &g_array_index (self->available_filters, struct VaFilter, i);
|
|
|
|
if (filter->type == type) {
|
|
if (filter->num_caps > 0)
|
|
ret = &filter->caps;
|
|
else if (type == VAProcFilterSkinToneEnhancement && filter->num_caps == 0)
|
|
ret = (gpointer) & i965_ste_caps;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ret && filter && num_caps)
|
|
*num_caps = filter->num_caps;
|
|
GST_OBJECT_UNLOCK (self);
|
|
|
|
return ret;
|
|
}
|
|
|
|
guint32
|
|
gst_va_filter_get_mem_types (GstVaFilter * self)
|
|
{
|
|
guint32 ret;
|
|
|
|
g_return_val_if_fail (GST_IS_VA_FILTER (self), 0);
|
|
|
|
GST_OBJECT_LOCK (self);
|
|
ret = self->mem_types;
|
|
GST_OBJECT_UNLOCK (self);
|
|
|
|
return ret;
|
|
}
|
|
|
|
GArray *
|
|
gst_va_filter_get_surface_formats (GstVaFilter * self)
|
|
{
|
|
GArray *ret;
|
|
|
|
g_return_val_if_fail (GST_IS_VA_FILTER (self), NULL);
|
|
|
|
GST_OBJECT_LOCK (self);
|
|
ret = self->surface_formats ? g_array_ref (self->surface_formats) : NULL;
|
|
GST_OBJECT_UNLOCK (self);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
_from_video_orientation_method (GstVideoOrientationMethod orientation,
|
|
guint * mirror, guint * rotation)
|
|
{
|
|
switch (orientation) {
|
|
case GST_VIDEO_ORIENTATION_IDENTITY:
|
|
*mirror = VA_MIRROR_NONE;
|
|
*rotation = VA_ROTATION_NONE;
|
|
break;
|
|
case GST_VIDEO_ORIENTATION_HORIZ:
|
|
*mirror = VA_MIRROR_HORIZONTAL;
|
|
*rotation = VA_ROTATION_NONE;
|
|
break;
|
|
case GST_VIDEO_ORIENTATION_VERT:
|
|
*mirror = VA_MIRROR_VERTICAL;
|
|
*rotation = VA_ROTATION_NONE;
|
|
break;
|
|
case GST_VIDEO_ORIENTATION_90R:
|
|
*mirror = VA_MIRROR_NONE;
|
|
*rotation = VA_ROTATION_90;
|
|
break;
|
|
case GST_VIDEO_ORIENTATION_180:
|
|
*mirror = VA_MIRROR_NONE;
|
|
*rotation = VA_ROTATION_180;
|
|
break;
|
|
case GST_VIDEO_ORIENTATION_90L:
|
|
*mirror = VA_MIRROR_NONE;
|
|
*rotation = VA_ROTATION_270;
|
|
break;
|
|
case GST_VIDEO_ORIENTATION_UL_LR:
|
|
*mirror = VA_MIRROR_HORIZONTAL;
|
|
*rotation = VA_ROTATION_90;
|
|
break;
|
|
case GST_VIDEO_ORIENTATION_UR_LL:
|
|
*mirror = VA_MIRROR_VERTICAL;
|
|
*rotation = VA_ROTATION_90;
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_va_filter_set_orientation (GstVaFilter * self,
|
|
GstVideoOrientationMethod orientation)
|
|
{
|
|
guint32 mirror = VA_MIRROR_NONE, rotation = VA_ROTATION_NONE;
|
|
guint32 mirror_flags, rotation_flags;
|
|
|
|
if (!gst_va_filter_is_open (self))
|
|
return FALSE;
|
|
|
|
if (!_from_video_orientation_method (orientation, &mirror, &rotation))
|
|
return FALSE;
|
|
|
|
GST_OBJECT_LOCK (self);
|
|
mirror_flags = self->pipeline_caps.mirror_flags;
|
|
GST_OBJECT_UNLOCK (self);
|
|
|
|
if (mirror != VA_MIRROR_NONE && !(mirror_flags & mirror))
|
|
return FALSE;
|
|
|
|
GST_OBJECT_LOCK (self);
|
|
rotation_flags = self->pipeline_caps.rotation_flags;
|
|
GST_OBJECT_UNLOCK (self);
|
|
|
|
if (rotation != VA_ROTATION_NONE && !(rotation_flags & (1 << rotation)))
|
|
return FALSE;
|
|
|
|
GST_OBJECT_LOCK (self);
|
|
self->orientation = orientation;
|
|
self->mirror = mirror;
|
|
self->rotation = rotation;
|
|
GST_OBJECT_UNLOCK (self);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GstVideoOrientationMethod
|
|
gst_va_filter_get_orientation (GstVaFilter * self)
|
|
{
|
|
GstVideoOrientationMethod ret;
|
|
|
|
GST_OBJECT_LOCK (self);
|
|
ret = self->orientation;
|
|
GST_OBJECT_UNLOCK (self);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
gst_va_filter_enable_cropping (GstVaFilter * self, gboolean cropping)
|
|
{
|
|
GST_OBJECT_LOCK (self);
|
|
if (cropping != self->crop_enabled)
|
|
self->crop_enabled = cropping;
|
|
GST_OBJECT_UNLOCK (self);
|
|
}
|
|
|
|
static inline GstCaps *
|
|
_create_base_caps (GstVaFilter * self)
|
|
{
|
|
/* XXX(victor): remove interlace-mode when deinterlacing is
|
|
* supported */
|
|
return gst_caps_new_simple ("video/x-raw", "width", GST_TYPE_INT_RANGE,
|
|
self->min_width, self->max_width, "height", GST_TYPE_INT_RANGE,
|
|
self->min_height, self->max_height, "interlace-mode", G_TYPE_STRING,
|
|
"progressive", NULL);
|
|
}
|
|
|
|
GstCaps *
|
|
gst_va_filter_get_caps (GstVaFilter * self)
|
|
{
|
|
GArray *surface_formats = NULL, *image_formats = NULL;
|
|
GstCaps *caps, *base_caps, *feature_caps;
|
|
GstCapsFeatures *features;
|
|
guint32 mem_types;
|
|
|
|
g_return_val_if_fail (GST_IS_VA_FILTER (self), NULL);
|
|
|
|
if (!gst_va_filter_is_open (self))
|
|
return NULL;
|
|
|
|
surface_formats = gst_va_filter_get_surface_formats (self);
|
|
if (!surface_formats)
|
|
return NULL;
|
|
|
|
base_caps = _create_base_caps (self);
|
|
|
|
if (!gst_caps_set_format_array (base_caps, surface_formats))
|
|
goto fail;
|
|
|
|
g_array_unref (surface_formats);
|
|
|
|
caps = gst_caps_new_empty ();
|
|
|
|
mem_types = gst_va_filter_get_mem_types (self);
|
|
|
|
if (mem_types & VA_SURFACE_ATTRIB_MEM_TYPE_VA) {
|
|
feature_caps = gst_caps_copy (base_caps);
|
|
features = gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_VA);
|
|
gst_caps_set_features_simple (feature_caps, features);
|
|
caps = gst_caps_merge (caps, feature_caps);
|
|
}
|
|
if (mem_types & VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME
|
|
|| mem_types & VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2) {
|
|
feature_caps = gst_caps_copy (base_caps);
|
|
features = gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_DMABUF);
|
|
gst_caps_set_features_simple (feature_caps, features);
|
|
caps = gst_caps_merge (caps, feature_caps);
|
|
}
|
|
|
|
gst_caps_unref (base_caps);
|
|
|
|
base_caps = _create_base_caps (self);
|
|
|
|
GST_OBJECT_LOCK (self);
|
|
image_formats =
|
|
self->image_formats ? g_array_ref (self->image_formats) : NULL;
|
|
GST_OBJECT_UNLOCK (self);
|
|
|
|
if (image_formats) {
|
|
if (!gst_caps_set_format_array (base_caps, image_formats))
|
|
goto fail;
|
|
g_array_unref (image_formats);
|
|
}
|
|
|
|
return gst_caps_merge (caps, base_caps);
|
|
|
|
fail:
|
|
{
|
|
g_clear_pointer (&surface_formats, g_array_unref);
|
|
g_clear_pointer (&image_formats, g_array_unref);
|
|
gst_caps_unref (base_caps);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* from va_vpp.h */
|
|
/* *INDENT-OFF* */
|
|
static const struct ColorPropertiesMap
|
|
{
|
|
VAProcColorStandardType standard;
|
|
guint8 primaries;
|
|
guint8 transfer;
|
|
guint8 matrix;
|
|
} color_properties_map[] = {
|
|
{ VAProcColorStandardBT601, 5, 6, 5 },
|
|
{ VAProcColorStandardBT601, 6, 6, 6 },
|
|
{ VAProcColorStandardBT709, 1, 1, 1 },
|
|
{ VAProcColorStandardBT470M, 4, 4, 4 },
|
|
{ VAProcColorStandardBT470BG, 5, 5, 5 },
|
|
{ VAProcColorStandardSMPTE170M, 6, 6, 6 },
|
|
{ VAProcColorStandardSMPTE240M, 7, 7, 7 },
|
|
{ VAProcColorStandardGenericFilm, 8, 1, 1 },
|
|
{ VAProcColorStandardSRGB, 1, 13, 0 },
|
|
/* { VAProcColorStandardSTRGB, ?, ?, ? }, */
|
|
{ VAProcColorStandardXVYCC601, 1, 11, 5 },
|
|
{ VAProcColorStandardXVYCC709, 1, 11, 1 },
|
|
{ VAProcColorStandardBT2020, 9, 14, 9 },
|
|
};
|
|
/* *INDENT-ON* */
|
|
|
|
static guint8
|
|
_get_chroma_siting (GstVideoChromaSite chrome_site)
|
|
{
|
|
/* *INDENT-OFF* */
|
|
static const struct ChromaSiteMap {
|
|
GstVideoChromaSite gst;
|
|
guint8 va;
|
|
} chroma_site_map[] = {
|
|
{ GST_VIDEO_CHROMA_SITE_UNKNOWN, VA_CHROMA_SITING_UNKNOWN },
|
|
{ GST_VIDEO_CHROMA_SITE_NONE, VA_CHROMA_SITING_VERTICAL_CENTER
|
|
| VA_CHROMA_SITING_HORIZONTAL_CENTER },
|
|
{ GST_VIDEO_CHROMA_SITE_H_COSITED, VA_CHROMA_SITING_VERTICAL_CENTER
|
|
| VA_CHROMA_SITING_HORIZONTAL_LEFT },
|
|
{ GST_VIDEO_CHROMA_SITE_V_COSITED, VA_CHROMA_SITING_VERTICAL_TOP
|
|
| VA_CHROMA_SITING_VERTICAL_BOTTOM },
|
|
{ GST_VIDEO_CHROMA_SITE_COSITED, VA_CHROMA_SITING_VERTICAL_CENTER
|
|
| VA_CHROMA_SITING_HORIZONTAL_LEFT
|
|
| VA_CHROMA_SITING_VERTICAL_TOP
|
|
| VA_CHROMA_SITING_VERTICAL_BOTTOM },
|
|
{ GST_VIDEO_CHROMA_SITE_JPEG, VA_CHROMA_SITING_VERTICAL_CENTER
|
|
| VA_CHROMA_SITING_HORIZONTAL_CENTER },
|
|
{ GST_VIDEO_CHROMA_SITE_MPEG2, VA_CHROMA_SITING_VERTICAL_CENTER
|
|
| VA_CHROMA_SITING_HORIZONTAL_LEFT },
|
|
{ GST_VIDEO_CHROMA_SITE_DV, VA_CHROMA_SITING_VERTICAL_TOP
|
|
| VA_CHROMA_SITING_HORIZONTAL_LEFT },
|
|
};
|
|
/* *INDENT-ON* */
|
|
guint i;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (chroma_site_map); i++) {
|
|
if (chrome_site == chroma_site_map[i].gst)
|
|
return chroma_site_map[i].va;
|
|
}
|
|
|
|
return VA_CHROMA_SITING_UNKNOWN;
|
|
}
|
|
|
|
static guint8
|
|
_get_color_range (GstVideoColorRange range)
|
|
{
|
|
/* *INDENT-OFF* */
|
|
static const struct ColorRangeMap {
|
|
GstVideoColorRange gst;
|
|
guint8 va;
|
|
} color_range_map[] = {
|
|
{ GST_VIDEO_COLOR_RANGE_UNKNOWN, VA_SOURCE_RANGE_UNKNOWN },
|
|
{ GST_VIDEO_COLOR_RANGE_0_255, VA_SOURCE_RANGE_FULL },
|
|
{ GST_VIDEO_COLOR_RANGE_16_235, VA_SOURCE_RANGE_REDUCED },
|
|
};
|
|
/* *INDENT-ON* */
|
|
guint i;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (color_range_map); i++) {
|
|
if (range == color_range_map[i].gst)
|
|
return color_range_map[i].va;
|
|
}
|
|
|
|
return VA_SOURCE_RANGE_UNKNOWN;
|
|
}
|
|
|
|
static VAProcColorStandardType
|
|
_gst_video_colorimetry_to_va (const GstVideoColorimetry * const colorimetry)
|
|
{
|
|
if (!colorimetry
|
|
|| colorimetry->primaries == GST_VIDEO_COLOR_PRIMARIES_UNKNOWN)
|
|
return VAProcColorStandardNone;
|
|
|
|
if (gst_video_colorimetry_matches (colorimetry, GST_VIDEO_COLORIMETRY_BT709))
|
|
return VAProcColorStandardBT709;
|
|
|
|
/* NOTE: VAProcColorStandardBT2020 in VAAPI is the same as
|
|
* GST_VIDEO_COLORIMETRY_BT2020_10 in gstreamer. */
|
|
if (gst_video_colorimetry_matches (colorimetry,
|
|
GST_VIDEO_COLORIMETRY_BT2020_10) ||
|
|
gst_video_colorimetry_matches (colorimetry, GST_VIDEO_COLORIMETRY_BT2020))
|
|
return VAProcColorStandardBT2020;
|
|
|
|
if (gst_video_colorimetry_matches (colorimetry, GST_VIDEO_COLORIMETRY_BT601))
|
|
return VAProcColorStandardBT601;
|
|
|
|
if (gst_video_colorimetry_matches (colorimetry,
|
|
GST_VIDEO_COLORIMETRY_SMPTE240M))
|
|
return VAProcColorStandardSMPTE240M;
|
|
|
|
if (gst_video_colorimetry_matches (colorimetry, GST_VIDEO_COLORIMETRY_SRGB))
|
|
return VAProcColorStandardSRGB;
|
|
|
|
return VAProcColorStandardNone;
|
|
}
|
|
|
|
static void
|
|
_config_color_properties (VAProcColorStandardType * std,
|
|
VAProcColorProperties * props, const GstVideoInfo * info,
|
|
VAProcColorStandardType * standards, guint32 num_standards)
|
|
{
|
|
GstVideoColorimetry colorimetry = GST_VIDEO_INFO_COLORIMETRY (info);
|
|
VAProcColorStandardType best;
|
|
gboolean has_explicit;
|
|
guint i, j, k;
|
|
gint score, bestscore = -1, worstscore;
|
|
|
|
best = _gst_video_colorimetry_to_va (&colorimetry);
|
|
|
|
has_explicit = FALSE;
|
|
for (i = 0; i < num_standards; i++) {
|
|
/* Find the exact match standard. */
|
|
if (standards[i] != VAProcColorStandardNone && standards[i] == best)
|
|
break;
|
|
|
|
if (standards[i] == VAProcColorStandardExplicit)
|
|
has_explicit = TRUE;
|
|
}
|
|
|
|
if (i < num_standards) {
|
|
*std = best;
|
|
goto set_properties;
|
|
} else if (has_explicit) {
|
|
*std = VAProcColorStandardExplicit;
|
|
goto set_properties;
|
|
}
|
|
|
|
worstscore = 4 * (colorimetry.matrix != GST_VIDEO_COLOR_MATRIX_UNKNOWN
|
|
&& colorimetry.matrix != GST_VIDEO_COLOR_MATRIX_RGB)
|
|
+ 2 * (colorimetry.transfer != GST_VIDEO_TRANSFER_UNKNOWN)
|
|
+ (colorimetry.primaries != GST_VIDEO_COLOR_PRIMARIES_UNKNOWN);
|
|
|
|
if (worstscore == 0) {
|
|
/* No properties specified, there's not a useful choice. */
|
|
*std = VAProcColorStandardNone;
|
|
*props = (VAProcColorProperties) {
|
|
};
|
|
|
|
return;
|
|
}
|
|
|
|
best = VAProcColorStandardNone;
|
|
k = -1;
|
|
for (i = 0; i < num_standards; i++) {
|
|
for (j = 0; j < G_N_ELEMENTS (color_properties_map); j++) {
|
|
if (color_properties_map[j].standard != standards[i])
|
|
continue;
|
|
|
|
score = 0;
|
|
if (colorimetry.matrix != GST_VIDEO_COLOR_MATRIX_UNKNOWN
|
|
&& colorimetry.matrix != GST_VIDEO_COLOR_MATRIX_RGB)
|
|
score += 4 * (colorimetry.matrix != color_properties_map[j].matrix);
|
|
if (colorimetry.transfer != GST_VIDEO_TRANSFER_UNKNOWN)
|
|
score += 2 * (colorimetry.transfer != color_properties_map[j].transfer);
|
|
if (colorimetry.primaries != GST_VIDEO_COLOR_PRIMARIES_UNKNOWN)
|
|
score += (colorimetry.primaries != color_properties_map[j].primaries);
|
|
|
|
if (score < worstscore && (bestscore == -1 || score < bestscore)) {
|
|
bestscore = score;
|
|
best = color_properties_map[j].standard;
|
|
k = j;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (best != VAProcColorStandardNone) {
|
|
*std = best;
|
|
colorimetry.matrix = color_properties_map[k].matrix;
|
|
colorimetry.transfer = color_properties_map[k].transfer;
|
|
colorimetry.primaries = color_properties_map[k].primaries;
|
|
}
|
|
|
|
set_properties:
|
|
/* *INDENT-OFF* */
|
|
*props = (VAProcColorProperties) {
|
|
.chroma_sample_location =
|
|
_get_chroma_siting (GST_VIDEO_INFO_CHROMA_SITE (info)),
|
|
.color_range = _get_color_range (colorimetry.range),
|
|
.colour_primaries =
|
|
gst_video_color_primaries_to_iso (colorimetry.primaries),
|
|
.transfer_characteristics =
|
|
gst_video_transfer_function_to_iso (colorimetry.transfer),
|
|
.matrix_coefficients =
|
|
gst_video_color_matrix_to_iso (colorimetry.matrix),
|
|
};
|
|
/* *INDENT-ON* */
|
|
}
|
|
|
|
gboolean
|
|
gst_va_filter_set_video_info (GstVaFilter * self, GstVideoInfo * in_info,
|
|
GstVideoInfo * out_info)
|
|
{
|
|
g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
|
|
g_return_val_if_fail (out_info && in_info, FALSE);
|
|
|
|
if (!gst_va_filter_is_open (self))
|
|
return FALSE;
|
|
|
|
GST_OBJECT_LOCK (self);
|
|
/* *INDENT-OFF* */
|
|
self->input_region = (VARectangle) {
|
|
.width = GST_VIDEO_INFO_WIDTH (in_info),
|
|
.height = GST_VIDEO_INFO_HEIGHT (in_info),
|
|
};
|
|
|
|
self->output_region = (VARectangle) {
|
|
.width = GST_VIDEO_INFO_WIDTH (out_info),
|
|
.height = GST_VIDEO_INFO_HEIGHT (out_info),
|
|
};
|
|
/* *INDENT-ON* */
|
|
|
|
_config_color_properties (&self->input_color_standard,
|
|
&self->input_color_properties, in_info,
|
|
self->pipeline_caps.input_color_standards,
|
|
self->pipeline_caps.num_input_color_standards);
|
|
_config_color_properties (&self->output_color_standard,
|
|
&self->output_color_properties, out_info,
|
|
self->pipeline_caps.output_color_standards,
|
|
self->pipeline_caps.num_output_color_standards);
|
|
GST_OBJECT_UNLOCK (self);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
_query_pipeline_caps (GstVaFilter * self, GArray * filters,
|
|
VAProcPipelineCaps * caps)
|
|
{
|
|
VABufferID *va_filters = NULL;
|
|
VADisplay dpy;
|
|
VAStatus status;
|
|
guint32 num_filters = 0;
|
|
|
|
GST_OBJECT_LOCK (self);
|
|
if (filters) {
|
|
num_filters = filters->len;
|
|
va_filters = (num_filters > 0) ? (VABufferID *) filters->data : NULL;
|
|
}
|
|
GST_OBJECT_UNLOCK (self);
|
|
|
|
dpy = gst_va_display_get_va_dpy (self->display);
|
|
|
|
gst_va_display_lock (self->display);
|
|
status = vaQueryVideoProcPipelineCaps (dpy, self->context, va_filters,
|
|
num_filters, caps);
|
|
gst_va_display_unlock (self->display);
|
|
|
|
if (status != VA_STATUS_SUCCESS) {
|
|
GST_ERROR_OBJECT (self, "vaQueryVideoProcPipelineCaps: %s",
|
|
vaErrorStr (status));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_va_filter_add_deinterlace_buffer (GstVaFilter * self,
|
|
VAProcDeinterlacingType method, guint32 * forward, guint32 * backward)
|
|
{
|
|
GArray *filters = NULL;
|
|
VAProcFilterParameterBufferDeinterlacing params = {
|
|
.type = VAProcFilterDeinterlacing,
|
|
.algorithm = method,
|
|
};
|
|
VAProcPipelineCaps pipeline_caps = { 0, };
|
|
gboolean ret;
|
|
|
|
g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
|
|
|
|
if (!gst_va_filter_is_open (self))
|
|
return FALSE;
|
|
|
|
if (!(method != VAProcDeinterlacingNone
|
|
&& method != VAProcDeinterlacingCount))
|
|
return FALSE;
|
|
|
|
if (!gst_va_filter_add_filter_buffer (self, ¶ms, sizeof (params), 1))
|
|
return FALSE;
|
|
|
|
GST_OBJECT_LOCK (self);
|
|
if (self->filters)
|
|
filters = g_array_ref (self->filters);
|
|
GST_OBJECT_UNLOCK (self);
|
|
ret = _query_pipeline_caps (self, filters, &pipeline_caps);
|
|
if (filters)
|
|
g_array_unref (filters);
|
|
if (!ret)
|
|
return FALSE;
|
|
|
|
if (forward)
|
|
*forward = pipeline_caps.num_forward_references;
|
|
if (backward)
|
|
*backward = pipeline_caps.num_backward_references;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_va_filter_add_filter_buffer (GstVaFilter * self, gpointer data, gsize size,
|
|
guint num)
|
|
{
|
|
VABufferID buffer;
|
|
VADisplay dpy;
|
|
VAStatus status;
|
|
|
|
g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
|
|
g_return_val_if_fail (data && size > 0, FALSE);
|
|
|
|
if (!gst_va_filter_is_open (self))
|
|
return FALSE;
|
|
|
|
dpy = gst_va_display_get_va_dpy (self->display);
|
|
gst_va_display_lock (self->display);
|
|
status = vaCreateBuffer (dpy, self->context, VAProcFilterParameterBufferType,
|
|
size, num, data, &buffer);
|
|
gst_va_display_unlock (self->display);
|
|
if (status != VA_STATUS_SUCCESS) {
|
|
GST_ERROR_OBJECT (self, "vaCreateBuffer: %s", vaErrorStr (status));
|
|
return FALSE;
|
|
}
|
|
|
|
/* lazy creation */
|
|
GST_OBJECT_LOCK (self);
|
|
if (!self->filters)
|
|
self->filters = g_array_sized_new (FALSE, FALSE, sizeof (VABufferID), 16);
|
|
|
|
g_array_append_val (self->filters, buffer);
|
|
GST_OBJECT_UNLOCK (self);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
_destroy_filters_unlocked (GstVaFilter * self)
|
|
{
|
|
VABufferID buffer;
|
|
VADisplay dpy;
|
|
VAStatus status;
|
|
gboolean ret = TRUE;
|
|
guint i;
|
|
|
|
GST_TRACE_OBJECT (self, "Destroying %u filter buffers", self->filters->len);
|
|
|
|
dpy = gst_va_display_get_va_dpy (self->display);
|
|
|
|
for (i = 0; i < self->filters->len; i++) {
|
|
buffer = g_array_index (self->filters, VABufferID, i);
|
|
|
|
gst_va_display_lock (self->display);
|
|
status = vaDestroyBuffer (dpy, buffer);
|
|
gst_va_display_unlock (self->display);
|
|
if (status != VA_STATUS_SUCCESS) {
|
|
ret = FALSE;
|
|
GST_WARNING_OBJECT (self, "Failed to destroy filter buffer: %s",
|
|
vaErrorStr (status));
|
|
}
|
|
}
|
|
|
|
self->filters = g_array_set_size (self->filters, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
gboolean
|
|
gst_va_filter_drop_filter_buffers (GstVaFilter * self)
|
|
{
|
|
gboolean ret = TRUE;
|
|
|
|
GST_OBJECT_LOCK (self);
|
|
if (self->filters)
|
|
ret = _destroy_filters_unlocked (self);
|
|
GST_OBJECT_UNLOCK (self);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
_fill_va_sample (GstVaFilter * self, GstVaSample * sample,
|
|
GstPadDirection direction)
|
|
{
|
|
GstVideoCropMeta *crop = NULL;
|
|
|
|
if (sample->buffer)
|
|
sample->surface = gst_va_buffer_get_surface (sample->buffer);
|
|
if (sample->surface == VA_INVALID_ID)
|
|
return FALSE;
|
|
|
|
/* @FIXME: in gallium vaQuerySurfaceStatus only seems to work with
|
|
* encoder's surfaces */
|
|
if (!GST_VA_DISPLAY_IS_IMPLEMENTATION (self->display, MESA_GALLIUM)) {
|
|
if (!va_check_surface (self->display, sample->surface))
|
|
return FALSE;
|
|
}
|
|
|
|
/* XXX: cropping occurs only in input frames */
|
|
if (direction == GST_PAD_SRC) {
|
|
GST_OBJECT_LOCK (self);
|
|
sample->rect = self->output_region;
|
|
GST_OBJECT_UNLOCK (self);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* if buffer has crop meta, its real size is in video meta */
|
|
if (sample->buffer)
|
|
crop = gst_buffer_get_video_crop_meta (sample->buffer);
|
|
|
|
GST_OBJECT_LOCK (self);
|
|
if (crop && self->crop_enabled) {
|
|
/* *INDENT-OFF* */
|
|
sample->rect = (VARectangle) {
|
|
.x = crop->x,
|
|
.y = crop->y,
|
|
.width = crop->width,
|
|
.height = crop->height
|
|
};
|
|
/* *INDENT-ON* */
|
|
} else {
|
|
sample->rect = self->input_region;
|
|
}
|
|
GST_OBJECT_UNLOCK (self);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
_create_pipeline_buffer (GstVaFilter * self, GstVaSample * src,
|
|
GstVaSample * dst, GArray * filters, VABufferID * buffer)
|
|
{
|
|
VADisplay dpy;
|
|
VAStatus status;
|
|
VABufferID *va_filters = NULL;
|
|
VAProcPipelineParameterBuffer params;
|
|
guint32 num_filters = 0;
|
|
|
|
GST_OBJECT_LOCK (self);
|
|
|
|
/* *INDENT-OFF* */
|
|
if (filters) {
|
|
num_filters = filters->len;
|
|
va_filters = (num_filters > 0) ? (VABufferID *) filters->data : NULL;
|
|
}
|
|
params = (VAProcPipelineParameterBuffer) {
|
|
.surface = src->surface,
|
|
.surface_region = &src->rect,
|
|
.surface_color_standard = self->input_color_standard,
|
|
.output_region = &dst->rect,
|
|
.output_background_color = 0xff000000, /* ARGB black */
|
|
.output_color_standard = self->output_color_standard,
|
|
.filters = va_filters,
|
|
.num_filters = num_filters,
|
|
.forward_references = src->forward_references,
|
|
.num_forward_references = src->num_forward_references,
|
|
.backward_references = src->backward_references,
|
|
.num_backward_references = src->num_backward_references,
|
|
.rotation_state = self->rotation,
|
|
.mirror_state = self->mirror,
|
|
.input_surface_flag = src->flags,
|
|
.output_surface_flag = dst->flags,
|
|
.input_color_properties = self->input_color_properties,
|
|
.output_color_properties = self->output_color_properties,
|
|
};
|
|
/* *INDENT-ON* */
|
|
|
|
GST_OBJECT_UNLOCK (self);
|
|
|
|
dpy = gst_va_display_get_va_dpy (self->display);
|
|
gst_va_display_lock (self->display);
|
|
status = vaCreateBuffer (dpy, self->context,
|
|
VAProcPipelineParameterBufferType, sizeof (params), 1, ¶ms, buffer);
|
|
gst_va_display_unlock (self->display);
|
|
if (status != VA_STATUS_SUCCESS) {
|
|
GST_ERROR_OBJECT (self, "vaCreateBuffer: %s", vaErrorStr (status));
|
|
return FALSE;
|
|
}
|
|
|
|
GST_TRACE_OBJECT (self, "Created VABufferID %#x with %u filters", *buffer,
|
|
num_filters);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_va_filter_process (GstVaFilter * self, GstVaSample * src, GstVaSample * dst)
|
|
{
|
|
GArray *filters = NULL;
|
|
VABufferID buffer;
|
|
VADisplay dpy;
|
|
VAProcPipelineCaps pipeline_caps = { 0, };
|
|
VAStatus status;
|
|
gboolean ret = FALSE;
|
|
|
|
g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
|
|
g_return_val_if_fail (src, FALSE);
|
|
g_return_val_if_fail (dst, FALSE);
|
|
|
|
if (!gst_va_filter_is_open (self))
|
|
return FALSE;
|
|
|
|
if (!(_fill_va_sample (self, src, GST_PAD_SINK)
|
|
&& _fill_va_sample (self, dst, GST_PAD_SRC)))
|
|
return FALSE;
|
|
|
|
GST_OBJECT_LOCK (self);
|
|
if (self->filters)
|
|
filters = g_array_ref (self->filters);
|
|
GST_OBJECT_UNLOCK (self);
|
|
|
|
if (!_query_pipeline_caps (self, filters, &pipeline_caps))
|
|
return FALSE;
|
|
|
|
if (!_create_pipeline_buffer (self, src, dst, filters, &buffer))
|
|
return FALSE;
|
|
|
|
if (filters)
|
|
g_array_unref (filters);
|
|
|
|
dpy = gst_va_display_get_va_dpy (self->display);
|
|
|
|
gst_va_display_lock (self->display);
|
|
status = vaBeginPicture (dpy, self->context, dst->surface);
|
|
gst_va_display_unlock (self->display);
|
|
if (status != VA_STATUS_SUCCESS) {
|
|
GST_ERROR_OBJECT (self, "vaBeginPicture: %s", vaErrorStr (status));
|
|
return FALSE;
|
|
}
|
|
|
|
gst_va_display_lock (self->display);
|
|
status = vaRenderPicture (dpy, self->context, &buffer, 1);
|
|
gst_va_display_unlock (self->display);
|
|
if (status != VA_STATUS_SUCCESS) {
|
|
GST_ERROR_OBJECT (self, "vaRenderPicture: %s", vaErrorStr (status));
|
|
goto fail_end_pic;
|
|
}
|
|
|
|
gst_va_display_lock (self->display);
|
|
status = vaEndPicture (dpy, self->context);
|
|
gst_va_display_unlock (self->display);
|
|
if (status != VA_STATUS_SUCCESS) {
|
|
GST_ERROR_OBJECT (self, "vaEndPicture: %s", vaErrorStr (status));
|
|
goto bail;
|
|
}
|
|
|
|
ret = TRUE;
|
|
|
|
bail:
|
|
gst_va_display_lock (self->display);
|
|
status = vaDestroyBuffer (dpy, buffer);
|
|
gst_va_display_unlock (self->display);
|
|
if (status != VA_STATUS_SUCCESS) {
|
|
GST_WARNING_OBJECT (self, "Failed to destroy pipeline buffer: %s",
|
|
vaErrorStr (status));
|
|
}
|
|
|
|
return ret;
|
|
|
|
fail_end_pic:
|
|
{
|
|
gst_va_display_lock (self->display);
|
|
status = vaEndPicture (dpy, self->context);
|
|
gst_va_display_unlock (self->display);
|
|
if (status != VA_STATUS_SUCCESS)
|
|
GST_ERROR_OBJECT (self, "vaEndPicture: %s", vaErrorStr (status));
|
|
goto bail;
|
|
}
|
|
}
|