[vlc-devel] [PATCH] video-filter: VAAPI: add filters
Victorien Le Couviour--Tuffet
victorien.lecouviour.tuffet at gmail.com
Tue Jun 20 10:40:46 CEST 2017
---
modules/hw/vaapi/Makefile.am | 8 +-
modules/hw/vaapi/filters.c | 1023 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 1030 insertions(+), 1 deletion(-)
create mode 100644 modules/hw/vaapi/filters.c
diff --git a/modules/hw/vaapi/Makefile.am b/modules/hw/vaapi/Makefile.am
index 3f2e446f94..6691f6d657 100644
--- a/modules/hw/vaapi/Makefile.am
+++ b/modules/hw/vaapi/Makefile.am
@@ -11,7 +11,13 @@ libvaapi_chroma_plugin_la_SOURCES = hw/vaapi/chroma.c hw/vaapi/vlc_vaapi.c hw/va
libvaapi_chroma_plugin_la_CFLAGS = $(AM_CFLAGS) $(LIBVA_CFLAGS)
libvaapi_chroma_plugin_la_LIBADD = $(AM_LIBADD) $(LIBVA_LIBS) libvlc_vaapi_instance.la
+libvaapi_filters_plugin_la_SOURCES = hw/vaapi/filters.c hw/vaapi/vlc_vaapi.c hw/vaapi/vlc_vaapi.h
+libvaapi_filters_plugin_la_CPPFLAGS = $(AM_CPPFLAGS)
+libvaapi_filters_plugin_la_CFLAGS = $(AM_CFLAGS) $(LIBVA_CFLAGS)
+libvaapi_filters_plugin_la_LIBADD = $(AM_LIBADD) $(LIBVA_LIBS) libvlc_vaapi_instance.la
+
if HAVE_VAAPI
vaapi_LTLIBRARIES = libvlc_vaapi_instance.la \
- libvaapi_chroma_plugin.la
+ libvaapi_chroma_plugin.la \
+ libvaapi_filters_plugin.la
endif
diff --git a/modules/hw/vaapi/filters.c b/modules/hw/vaapi/filters.c
new file mode 100644
index 0000000000..cec2739573
--- /dev/null
+++ b/modules/hw/vaapi/filters.c
@@ -0,0 +1,1023 @@
+/*****************************************************************************
+ * filters.c: VAAPI filters
+ *****************************************************************************
+ * Copyright (C) 2017 VLC authors, VideoLAN and VideoLabs
+ *
+ * Author: Victorien Le Couviour--Tuffet <victorien.lecouviour.tuffet at gmail.com>
+ *
+ * This program 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 program 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <assert.h>
+
+#include <vlc_common.h>
+#include <vlc_atomic.h>
+#include <vlc_filter.h>
+#include <vlc_plugin.h>
+#include "vlc_vaapi.h"
+
+/********************************
+ * Common structures and macros *
+ ********************************/
+
+struct va_filter_desc
+{
+ VADisplay dpy;
+ VAConfigID conf;
+ VAContextID ctx;
+ VABufferID buf;
+ VASurfaceID * surface_ids;
+};
+
+struct filter_sys_t
+{
+ struct va_filter_desc va;
+ picture_pool_t * dest_pics;
+ bool b_pipeline_fast;
+ void * p_data;
+};
+
+#define DEST_PICS_POOL_SZ 3
+
+struct range
+{
+ float min_value;
+ float max_value;
+};
+
+#define GET_DRV_SIGMA(vlc_sigma, vlc_range, drv_range) \
+ ((vlc_sigma - (vlc_range).min_value) * \
+ ((drv_range).max_value - (drv_range).min_value) / \
+ ((vlc_range).max_value - (vlc_range).min_value) + (drv_range).min_value)
+
+/***********************************
+ * Adjust structures and constants *
+ ***********************************/
+
+enum { ADJUST_CONT, ADJUST_LUM, ADJUST_HUE, ADJUST_SAT, NUM_ADJUST_MODES };
+
+static VAProcColorBalanceType const va_adjust_modes[NUM_ADJUST_MODES] =
+{
+ [ADJUST_CONT] = VAProcColorBalanceContrast,
+ [ADJUST_LUM] = VAProcColorBalanceBrightness,
+ [ADJUST_HUE] = VAProcColorBalanceHue,
+ [ADJUST_SAT] = VAProcColorBalanceSaturation
+};
+
+static char const *const adjust_params_names[NUM_ADJUST_MODES] =
+{
+ [ADJUST_CONT] = "contrast",
+ [ADJUST_LUM] = "brightness",
+ [ADJUST_HUE] = "hue",
+ [ADJUST_SAT] = "saturation"
+};
+
+static struct range const vlc_adjust_sigma_ranges[NUM_ADJUST_MODES] =
+{
+ [ADJUST_CONT] = { .0f, 2.f },
+ [ADJUST_LUM] = { .0f, 2.f },
+ [ADJUST_HUE] = { -180.f, +180.f },
+ [ADJUST_SAT] = { .0f, 3.f }
+};
+
+struct adjust_params
+{
+ struct
+ {
+ vlc_atomic_float drv_value;
+ VAProcFilterValueRange drv_range;
+ bool is_available;
+ } sigma[NUM_ADJUST_MODES];
+};
+
+struct adjust_data
+{
+ struct adjust_params params;
+ unsigned int num_available_modes;
+};
+
+/* Adaptation of VAAPI adjust filter so it looks more like the CPU one */
+static float
+adapt_adjust_sigma(char const * psz_var, float const sigma,
+ struct range const * p_range)
+{
+ if (!strcmp(psz_var, "contrast"))
+ {
+ struct range const adapt_range = { .0f, .35f };
+ return GET_DRV_SIGMA(sigma, *p_range, adapt_range);
+ }
+ else if (!strcmp(psz_var, "saturation"))
+ {
+ struct range const adapt_range = { .0f, 1.f };
+ return GET_DRV_SIGMA(sigma, *p_range, adapt_range);
+ }
+ return sigma;
+}
+
+/*****************************************
+ * Basic filter structures and constants *
+ *****************************************/
+
+static struct range const vlc_denoise_sigma_range = { .0f, 2.f };
+static struct range const vlc_sharpen_sigma_range = { .0f, 2.f };
+
+struct basic_filter_data
+{
+ struct
+ {
+ vlc_atomic_float drv_value;
+ VAProcFilterValueRange drv_range;
+ struct range const * p_vlc_range;
+ char * psz_name;
+ } sigma;
+
+ VAProcFilterType filter_type;
+};
+
+/****************************************
+ * Deinterlace structures and constants *
+ ****************************************/
+
+struct deint_mode
+{
+ char const * name;
+ VAProcDeinterlacingType type;
+};
+
+static struct deint_mode const deint_modes[VAProcDeinterlacingCount - 1] =
+{
+ { "x", VAProcDeinterlacingMotionCompensated },
+ { "ma", VAProcDeinterlacingMotionAdaptive },
+ { "bob", VAProcDeinterlacingBob },
+ { "weave", VAProcDeinterlacingWeave }
+};
+
+struct deint_data
+{
+ struct
+ {
+ picture_t ** pp_pics;
+ picture_t ** pp_cur_pic;
+ unsigned int num_pics;
+ unsigned int sz;
+ } history;
+
+ struct
+ {
+ VASurfaceID * surfaces;
+ unsigned int sz;
+ } backward_refs, forward_refs;
+};
+
+/********************
+ * Common functions *
+ ********************/
+
+static picture_t *
+Filter(filter_t * filter, picture_t * src,
+ void (*pf_update_va_filter_params)(void *, void *),
+ void (*pf_prepare_render_surface)(void *),
+ void (*pf_update_pipeline_params)(void *,
+ VAProcPipelineParameterBuffer *))
+{
+ filter_sys_t *const filter_sys = filter->p_sys;
+ picture_t *const dest = picture_pool_Wait(filter_sys->dest_pics);
+ if (!dest)
+ return NULL;
+
+ vlc_vaapi_PicAttachContext(dest);
+ picture_CopyProperties(dest, src);
+
+ void * p_va_params;
+
+ if (vlc_vaapi_MapBuffer(VLC_OBJECT(filter), filter_sys->va.dpy,
+ filter_sys->va.buf, &p_va_params))
+ goto error;
+
+ if (pf_update_va_filter_params)
+ pf_update_va_filter_params(filter_sys->p_data, p_va_params);
+
+ if (vlc_vaapi_UnmapBuffer(VLC_OBJECT(filter),
+ filter_sys->va.dpy, filter_sys->va.buf))
+ goto error;
+
+ if (vlc_vaapi_BeginPicture(VLC_OBJECT(filter),
+ filter_sys->va.dpy, filter_sys->va.ctx,
+ vlc_vaapi_PicGetSurface(dest)))
+ goto error;
+
+ if (pf_prepare_render_surface)
+ pf_prepare_render_surface(filter_sys->p_data);
+
+ VABufferID pipeline_buf = VA_INVALID_ID;
+ VAProcPipelineParameterBuffer * pipeline_params;
+
+ pipeline_buf =
+ vlc_vaapi_CreateBuffer(VLC_OBJECT(filter),
+ filter_sys->va.dpy, filter_sys->va.ctx,
+ VAProcPipelineParameterBufferType,
+ sizeof(*pipeline_params), 1, NULL);
+ if (pipeline_buf == VA_INVALID_ID)
+ goto error;
+
+ if (vlc_vaapi_MapBuffer(VLC_OBJECT(filter), filter_sys->va.dpy,
+ pipeline_buf, (void **)&pipeline_params))
+ goto error;
+
+ *pipeline_params = (typeof(*pipeline_params)){0};
+ pipeline_params->surface = vlc_vaapi_PicGetSurface(src);
+ pipeline_params->filters = &filter_sys->va.buf;
+ pipeline_params->num_filters = 1;
+ if (filter_sys->b_pipeline_fast)
+ pipeline_params->pipeline_flags = VA_PROC_PIPELINE_FAST;
+ if (pf_update_pipeline_params)
+ pf_update_pipeline_params(filter_sys->p_data, pipeline_params);
+
+ if (vlc_vaapi_UnmapBuffer(VLC_OBJECT(filter),
+ filter_sys->va.dpy, pipeline_buf))
+ goto error;
+
+ if (vlc_vaapi_RenderPicture(VLC_OBJECT(filter),
+ filter_sys->va.dpy, filter_sys->va.ctx,
+ &pipeline_buf, 1))
+ goto error;
+
+ if (vlc_vaapi_EndPicture(VLC_OBJECT(filter),
+ filter_sys->va.dpy, filter_sys->va.ctx))
+ goto error;
+
+ return dest;
+
+error:
+ if (pipeline_buf != VA_INVALID_ID)
+ vlc_vaapi_DestroyBuffer(VLC_OBJECT(filter),
+ filter_sys->va.dpy, pipeline_buf);
+ if (dest)
+ picture_Release(dest);
+ return NULL;
+}
+
+static int
+Open(filter_t * filter,
+ VAProcFilterType const filter_type,
+ VAProcPipelineCaps * p_pipeline_caps,
+ void * p_data,
+ int (*pf_init_filter_params)(filter_t *, void *, void **,
+ unsigned int *, unsigned int *),
+ int (*pf_use_pipeline_caps)(void *, VAProcPipelineCaps const *))
+{
+ filter_sys_t * filter_sys;
+
+ if (filter->fmt_out.video.i_chroma != VLC_CODEC_VAAPI_420 ||
+ !video_format_IsSimilar(&filter->fmt_out.video, &filter->fmt_in.video))
+ return VLC_EGENERIC;
+
+ filter_sys = calloc(1, sizeof(*filter_sys));
+ if (!filter_sys)
+ return VLC_ENOMEM;
+ filter->p_sys = filter_sys;
+
+ filter_sys->p_data = p_data;
+
+ filter_sys->va.conf = VA_INVALID_ID;
+ filter_sys->va.ctx = VA_INVALID_ID;
+ filter_sys->va.buf = VA_INVALID_ID;
+ filter_sys->va.dpy = vlc_vaapi_GetInstance();
+ if (!filter_sys->va.dpy)
+ goto error;
+
+ filter_sys->dest_pics =
+ vlc_vaapi_PoolNew(VLC_OBJECT(filter), filter_sys->va.dpy,
+ DEST_PICS_POOL_SZ, &filter_sys->va.surface_ids,
+ &filter->fmt_out.video,
+ VA_RT_FORMAT_YUV420, VA_FOURCC_NV12);
+ if (!filter_sys->dest_pics)
+ goto error;
+
+ filter_sys->va.conf =
+ vlc_vaapi_CreateConfigChecked(VLC_OBJECT(filter), filter_sys->va.dpy,
+ VAProfileNone, VAEntrypointVideoProc,
+ VA_FOURCC_NV12);
+ if (filter_sys->va.conf == VA_INVALID_ID)
+ goto error;
+
+ filter_sys->va.ctx =
+ vlc_vaapi_CreateContext(VLC_OBJECT(filter),
+ filter_sys->va.dpy, filter_sys->va.conf,
+ filter->fmt_out.video.i_width,
+ filter->fmt_out.video.i_height,
+ 0, filter_sys->va.surface_ids,
+ DEST_PICS_POOL_SZ);
+ if (filter_sys->va.ctx == VA_INVALID_ID)
+ goto error;
+
+ if (vlc_vaapi_IsVideoProcFilterAvailable(VLC_OBJECT(filter),
+ filter_sys->va.dpy,
+ filter_sys->va.ctx,
+ filter_type))
+ goto error;
+
+ void * p_va_params;
+ uint32_t i_sz_param;
+ uint32_t i_num_params;
+
+ if (pf_init_filter_params(filter, p_data,
+ &p_va_params, &i_sz_param, &i_num_params))
+ goto error;
+
+ filter_sys->va.buf =
+ vlc_vaapi_CreateBuffer(VLC_OBJECT(filter),
+ filter_sys->va.dpy, filter_sys->va.ctx,
+ VAProcFilterParameterBufferType,
+ i_sz_param, i_num_params, p_va_params);
+ free(p_va_params);
+ if (filter_sys->va.buf == VA_INVALID_ID)
+ goto error;
+
+ if (vlc_vaapi_QueryVideoProcPipelineCaps(VLC_OBJECT(filter),
+ filter_sys->va.dpy,
+ filter_sys->va.ctx,
+ &filter_sys->va.buf,
+ 1, p_pipeline_caps))
+ goto error;
+
+ filter_sys->b_pipeline_fast =
+ p_pipeline_caps->pipeline_flags & VA_PROC_PIPELINE_FAST;
+
+ if (pf_use_pipeline_caps &&
+ pf_use_pipeline_caps(p_data, p_pipeline_caps))
+ goto error;
+
+ return VLC_SUCCESS;
+
+error:
+ if (filter_sys->va.buf != VA_INVALID_ID)
+ vlc_vaapi_DestroyBuffer(VLC_OBJECT(filter),
+ filter_sys->va.dpy, filter_sys->va.buf);
+ if (filter_sys->va.ctx != VA_INVALID_ID)
+ vlc_vaapi_DestroyContext(VLC_OBJECT(filter),
+ filter_sys->va.dpy, filter_sys->va.ctx);
+ if (filter_sys->va.conf != VA_INVALID_ID)
+ vlc_vaapi_DestroyConfig(VLC_OBJECT(filter),
+ filter_sys->va.dpy, filter_sys->va.conf);
+ if (filter_sys->va.dpy)
+ vlc_vaapi_ReleaseInstance(filter_sys->va.dpy);
+ free(filter_sys);
+ return VLC_EGENERIC;
+}
+
+static void
+Close(vlc_object_t * obj, filter_sys_t * filter_sys)
+{
+ picture_pool_Release(filter_sys->dest_pics);
+ vlc_vaapi_DestroyBuffer(obj, filter_sys->va.dpy, filter_sys->va.buf);
+ vlc_vaapi_DestroyContext(obj, filter_sys->va.dpy, filter_sys->va.ctx);
+ vlc_vaapi_DestroyConfig(obj, filter_sys->va.dpy, filter_sys->va.conf);
+ vlc_vaapi_ReleaseInstance(filter_sys->va.dpy);
+ free(filter_sys);
+}
+
+static int
+FilterCallback(vlc_object_t * obj, char const * psz_var,
+ vlc_value_t oldval, vlc_value_t newval,
+ void * p_data)
+{ VLC_UNUSED(obj); VLC_UNUSED(oldval);
+ struct range const * p_vlc_range;
+ VAProcFilterValueRange const * p_drv_range;
+ vlc_atomic_float * p_drv_value;
+ bool b_found = false;
+ bool b_adjust = false;
+
+ for (unsigned int i = 0; i < NUM_ADJUST_MODES; ++i)
+ if (!strcmp(psz_var, adjust_params_names[i]))
+ {
+ struct adjust_data *const p_adjust_data = p_data;
+
+ if (!p_adjust_data->params.sigma[i].is_available)
+ return VLC_EGENERIC;
+
+ p_vlc_range = vlc_adjust_sigma_ranges + i;
+ p_drv_range = &p_adjust_data->params.sigma[i].drv_range;
+ p_drv_value = &p_adjust_data->params.sigma[i].drv_value;
+
+ b_adjust = true;
+ b_found = true;
+ }
+ if (!b_found)
+ if (!strcmp(psz_var, "denoise-sigma") ||
+ !strcmp(psz_var, "sharpen-sigma"))
+ {
+ struct basic_filter_data *const p_basic_filter_data = p_data;
+
+ p_vlc_range = p_basic_filter_data->sigma.p_vlc_range;
+ p_drv_range = &p_basic_filter_data->sigma.drv_range;
+ p_drv_value = &p_basic_filter_data->sigma.drv_value;
+
+ b_found = true;
+ }
+
+ if (!b_found)
+ return VLC_EGENERIC;
+
+ float vlc_sigma = VLC_CLIP(newval.f_float,
+ p_vlc_range->min_value,
+ p_vlc_range->max_value);
+
+ if (b_adjust)
+ vlc_sigma = adapt_adjust_sigma(psz_var, vlc_sigma, p_vlc_range);
+
+ float const drv_sigma = GET_DRV_SIGMA(vlc_sigma,
+ *p_vlc_range, *p_drv_range);
+
+ vlc_atomic_store_float(p_drv_value, drv_sigma);
+
+ return VLC_SUCCESS;
+}
+
+/********************
+ * Adjust functions *
+ ********************/
+
+static void
+Adjust_UpdateVAFilterParams(void * p_data, void * va_params)
+{
+ struct adjust_data *const p_adjust_data = p_data;
+ struct adjust_params *const p_adjust_params = &p_adjust_data->params;
+ VAProcFilterParameterBufferColorBalance *const p_va_params = va_params;
+
+ unsigned int i = 0;
+ for (unsigned int j = 0; j < NUM_ADJUST_MODES; ++j)
+ if (p_adjust_params->sigma[j].is_available)
+ p_va_params[i++].value =
+ vlc_atomic_load_float(&p_adjust_params->sigma[j].drv_value);
+}
+
+static picture_t *
+Adjust(filter_t * filter, picture_t * src)
+{
+ picture_t *const dest =
+ Filter(filter, src, Adjust_UpdateVAFilterParams, NULL, NULL);
+ picture_Release(src);
+ return dest;
+}
+
+static int
+OpenAdjust_InitFilterParams(filter_t * filter, void * p_data,
+ void ** pp_va_params,
+ uint32_t * p_va_param_sz,
+ uint32_t * p_num_va_params)
+{
+ struct adjust_data *const p_adjust_data = p_data;
+ struct adjust_params *const p_adjust_params = &p_adjust_data->params;
+ filter_sys_t *const filter_sys = filter->p_sys;
+ VAProcFilterCapColorBalance caps[VAProcColorBalanceCount];
+ unsigned int num_caps = VAProcColorBalanceCount;
+
+ if (vlc_vaapi_QueryVideoProcFilterCaps(VLC_OBJECT(filter),
+ filter_sys->va.dpy,
+ filter_sys->va.ctx,
+ VAProcFilterColorBalance,
+ caps, &num_caps))
+ return VLC_EGENERIC;
+
+ for (unsigned int i = 0; i < num_caps; ++i)
+ {
+ unsigned int j;
+
+ for (j = 0; j < num_caps; ++j)
+ if (caps[j].type == va_adjust_modes[i])
+ {
+ float vlc_sigma =
+ VLC_CLIP(var_InheritFloat(filter, adjust_params_names[i]),
+ vlc_adjust_sigma_ranges[i].min_value,
+ vlc_adjust_sigma_ranges[i].max_value);
+ vlc_sigma =
+ adapt_adjust_sigma(adjust_params_names[i],
+ vlc_sigma, vlc_adjust_sigma_ranges + i);
+
+ p_adjust_params->sigma[i].drv_range = caps[j].range;
+ p_adjust_params->sigma[i].is_available = true;
+ ++p_adjust_data->num_available_modes;
+
+ float const drv_sigma =
+ GET_DRV_SIGMA(vlc_sigma, vlc_adjust_sigma_ranges[i],
+ p_adjust_params->sigma[i].drv_range);
+
+ vlc_atomic_init_float(&p_adjust_params->sigma[i].drv_value,
+ drv_sigma);
+ break;
+ }
+ }
+
+ VAProcFilterParameterBufferColorBalance * p_va_params;
+
+ *p_va_param_sz = sizeof(typeof(*p_va_params));
+ *p_num_va_params = p_adjust_data->num_available_modes;
+
+ p_va_params = calloc(*p_num_va_params, *p_va_param_sz);
+ if (!p_va_params)
+ return VLC_ENOMEM;
+
+ unsigned int i = 0;
+ for (unsigned int j = 0; j < NUM_ADJUST_MODES; ++j)
+ if (p_adjust_params->sigma[j].is_available)
+ {
+ p_va_params[i].type = VAProcFilterColorBalance;
+ p_va_params[i++].attrib = va_adjust_modes[j];
+ }
+
+ *pp_va_params = p_va_params;
+
+ return VLC_SUCCESS;
+}
+
+static int
+OpenAdjust(vlc_object_t * obj)
+{
+ VAProcPipelineCaps pipeline_caps;
+ filter_t *const filter = (filter_t *)obj;
+ struct adjust_data *const p_data = calloc(1, sizeof(*p_data));
+ if (!p_data)
+ return VLC_ENOMEM;
+
+ for (unsigned int i = 0; i < NUM_ADJUST_MODES; ++i)
+ var_Create(obj, adjust_params_names[i],
+ VLC_VAR_FLOAT | VLC_VAR_DOINHERIT | VLC_VAR_ISCOMMAND);
+
+ if (Open(filter, VAProcFilterColorBalance, &pipeline_caps, p_data,
+ OpenAdjust_InitFilterParams, NULL))
+ goto error;
+
+ for (unsigned int i = 0; i < NUM_ADJUST_MODES; ++i)
+ var_AddCallback(obj, adjust_params_names[i], FilterCallback, p_data);
+
+ filter->pf_video_filter = Adjust;
+
+ return VLC_SUCCESS;
+
+error:
+ for (unsigned int i = 0; i < NUM_ADJUST_MODES; ++i)
+ var_Destroy(obj, adjust_params_names[i]);
+ free(p_data);
+ return VLC_EGENERIC;
+}
+
+static void
+CloseAdjust(vlc_object_t * obj)
+{
+ filter_t *const filter = (filter_t *)obj;
+ filter_sys_t *const filter_sys = filter->p_sys;
+
+ for (unsigned int i = 0; i < NUM_ADJUST_MODES; ++i)
+ {
+ var_DelCallback(obj, adjust_params_names[i],
+ FilterCallback, filter_sys->p_data);
+ var_Destroy(obj, adjust_params_names[i]);
+ }
+ free(filter_sys->p_data);
+ Close(obj, filter_sys);
+}
+
+/***************************
+ * Basic filters functions *
+ ***************************/
+
+static void
+BasicFilter_UpdateVAFilterParams(void * p_data, void * va_params)
+{
+ struct basic_filter_data *const p_basic_filter_data = p_data;
+ VAProcFilterParameterBuffer *const p_va_param = va_params;
+
+ p_va_param->value =
+ vlc_atomic_load_float(&p_basic_filter_data->sigma.drv_value);
+}
+
+static picture_t *
+BasicFilter(filter_t * filter, picture_t * src)
+{
+ picture_t *const dest =
+ Filter(filter, src, BasicFilter_UpdateVAFilterParams, NULL, NULL);
+ picture_Release(src);
+ return dest;
+}
+
+static int
+OpenBasicFilter_InitFilterParams(filter_t * filter, void * p_data,
+ void ** pp_va_params,
+ uint32_t * p_va_param_sz,
+ uint32_t * p_num_va_params)
+{
+ struct basic_filter_data *const p_basic_filter_data = p_data;
+ filter_sys_t *const filter_sys = filter->p_sys;
+ VAProcFilterCap caps;
+ unsigned int num_caps = 1;
+
+ if (vlc_vaapi_QueryVideoProcFilterCaps(VLC_OBJECT(filter),
+ filter_sys->va.dpy,
+ filter_sys->va.buf,
+ p_basic_filter_data->filter_type,
+ &caps, &num_caps)
+ || !num_caps)
+ return VLC_EGENERIC;
+
+ float const vlc_sigma =
+ VLC_CLIP(var_InheritFloat(filter, p_basic_filter_data->sigma.psz_name),
+ p_basic_filter_data->sigma.p_vlc_range->min_value,
+ p_basic_filter_data->sigma.p_vlc_range->max_value);
+
+ p_basic_filter_data->sigma.drv_range = caps.range;
+
+ float const drv_sigma =
+ GET_DRV_SIGMA(vlc_sigma, *p_basic_filter_data->sigma.p_vlc_range,
+ p_basic_filter_data->sigma.drv_range);
+
+ vlc_atomic_init_float(&p_basic_filter_data->sigma.drv_value, drv_sigma);
+
+ VAProcFilterParameterBuffer * p_va_param;
+
+ *p_va_param_sz = sizeof(*p_va_param);
+ *p_num_va_params = 1;
+
+ p_va_param = calloc(1, sizeof(*p_va_param));
+ if (!p_va_param)
+ return VLC_ENOMEM;
+
+ p_va_param->type = p_basic_filter_data->filter_type;
+ *pp_va_params = p_va_param;
+
+ return VLC_SUCCESS;
+}
+
+static int
+OpenBasicFilter(vlc_object_t * obj)
+{
+ VAProcPipelineCaps pipeline_caps;
+ filter_t *const filter = (filter_t *)obj;
+ assert(filter->psz_name);
+ struct basic_filter_data *const p_data = calloc(1, sizeof(*p_data));
+ if (!p_data)
+ return VLC_ENOMEM;
+
+ p_data->sigma.psz_name =
+ calloc(strlen(filter->psz_name) + strlen("-sigma") + 1, sizeof(char));
+ if (!p_data->sigma.psz_name)
+ goto error;
+
+ strcpy(p_data->sigma.psz_name, filter->psz_name);
+ strcat(p_data->sigma.psz_name, "-sigma");
+
+ if (!strcmp(filter->psz_name, "denoise"))
+ {
+ p_data->filter_type = VAProcFilterNoiseReduction;
+ p_data->sigma.p_vlc_range = &vlc_denoise_sigma_range;
+ }
+ else if (!strcmp(filter->psz_name, "sharpen"))
+ {
+ p_data->filter_type = VAProcFilterSharpening;
+ p_data->sigma.p_vlc_range = &vlc_sharpen_sigma_range;
+ }
+
+ var_Create(obj, p_data->sigma.psz_name,
+ VLC_VAR_FLOAT | VLC_VAR_DOINHERIT | VLC_VAR_ISCOMMAND);
+
+ if (Open(filter, p_data->filter_type, &pipeline_caps, p_data,
+ OpenBasicFilter_InitFilterParams, NULL))
+ goto error;
+
+ var_AddCallback(obj, p_data->sigma.psz_name, FilterCallback, p_data);
+
+ filter->pf_video_filter = BasicFilter;
+
+ return VLC_SUCCESS;
+
+error:
+ var_Destroy(obj, p_data->sigma.psz_name);
+ if (p_data->sigma.psz_name)
+ free(p_data->sigma.psz_name);
+ free(p_data);
+ return VLC_EGENERIC;
+}
+
+static void
+CloseBasicFilter(vlc_object_t * obj)
+{
+ filter_t *const filter = (filter_t *)obj;
+ filter_sys_t *const filter_sys = filter->p_sys;
+ struct basic_filter_data *const p_data = filter_sys->p_data;
+
+ var_DelCallback(obj, p_data->sigma.psz_name, FilterCallback, p_data);
+ var_Destroy(obj, p_data->sigma.psz_name);
+ free(p_data->sigma.psz_name);
+ free(p_data);
+ Close(obj, filter_sys);
+}
+
+/*************************
+ * Deinterlace functions *
+ *************************/
+
+static picture_t *
+Deinterlace_UpdateHistory(struct deint_data * p_deint_data, picture_t * src)
+{
+ if (p_deint_data->history.num_pics == p_deint_data->history.sz)
+ {
+ picture_Release(*p_deint_data->history.pp_pics);
+ memmove(p_deint_data->history.pp_pics, p_deint_data->history.pp_pics + 1,
+ --p_deint_data->history.num_pics * sizeof(picture_t *));
+ }
+ p_deint_data->history.pp_pics[p_deint_data->history.num_pics++] = src;
+
+ return *p_deint_data->history.pp_cur_pic;
+}
+
+static void
+Deinterlace_UpdateReferenceFrames(void * p_data)
+{
+ struct deint_data *const p_deint_data = p_data;
+
+ if (p_deint_data->backward_refs.sz)
+ for (unsigned int i = 0; i < p_deint_data->backward_refs.sz; ++i)
+ p_deint_data->backward_refs.surfaces[i] =
+ vlc_vaapi_PicGetSurface(p_deint_data->history.pp_pics[i]);
+
+ if (p_deint_data->forward_refs.sz)
+ for (unsigned int i = 0; i < p_deint_data->forward_refs.sz; ++i)
+ {
+ unsigned int const idx = p_deint_data->backward_refs.sz + 1 + i;
+
+ p_deint_data->forward_refs.surfaces[i] =
+ vlc_vaapi_PicGetSurface(p_deint_data->history.pp_pics[idx]);
+ }
+}
+
+static void
+Deinterlace_UpdatePipelineParams
+(void * p_data, VAProcPipelineParameterBuffer * pipeline_param)
+{
+ struct deint_data *const p_deint_data = p_data;
+
+ pipeline_param->filter_flags =
+ p_deint_data->history.pp_cur_pic[0]->b_top_field_first ?
+ 0 : VA_DEINTERLACING_BOTTOM_FIELD_FIRST;
+
+ pipeline_param->backward_references = p_deint_data->backward_refs.surfaces;
+ pipeline_param->forward_references = p_deint_data->forward_refs.surfaces;
+ pipeline_param->num_backward_references = p_deint_data->backward_refs.sz;
+ pipeline_param->num_forward_references = p_deint_data->forward_refs.sz;
+}
+
+static picture_t *
+Deinterlace(filter_t * filter, picture_t * src)
+{
+ filter_sys_t *const filter_sys = filter->p_sys;
+ struct deint_data *const p_deint_data = filter_sys->p_data;
+
+ src = Deinterlace_UpdateHistory(p_deint_data, src);
+ if (p_deint_data->history.num_pics < p_deint_data->history.sz)
+ return NULL;
+
+ picture_t *const dest =
+ Filter(filter, src, NULL,
+ Deinterlace_UpdateReferenceFrames,
+ Deinterlace_UpdatePipelineParams);
+
+ if (dest)
+ dest->b_progressive = true;
+
+ return dest;
+}
+
+static inline bool
+OpenDeinterlace_IsValidType(filter_t * filter,
+ VAProcDeinterlacingType const caps[],
+ unsigned int num_caps,
+ struct deint_mode const * deint_mode,
+ bool const fallback)
+{
+ for (unsigned int j = 0; j < num_caps; ++j)
+ if (caps[j] == deint_mode->type)
+ {
+ if (fallback)
+ msg_Info(filter, "falling back to %s algorithm",
+ deint_mode->name);
+ return true;
+ }
+ return false;
+}
+
+static inline int
+OpenDeinterlace_GetMode(filter_t * filter, char const * deint_mode,
+ VAProcDeinterlacingType * p_deint_mode,
+ VAProcDeinterlacingType const caps[],
+ unsigned int num_caps)
+{
+ if (deint_mode && strcmp(deint_mode, "auto"))
+ {
+ for (unsigned int i = 0; i < VAProcDeinterlacingCount - 1; ++i)
+ if (!strcmp(deint_mode, deint_modes[i].name))
+ {
+ if (OpenDeinterlace_IsValidType(filter, caps, num_caps,
+ deint_modes + i, false))
+ {
+ *p_deint_mode = deint_modes[i].type;
+ msg_Dbg(filter, "using %s deinterlace method",
+ deint_modes[i].name);
+ return VLC_SUCCESS;
+ }
+ break;
+ }
+ msg_Err(filter, "%s algorithm not available", deint_mode);
+ return VLC_EGENERIC;
+ }
+
+ for (unsigned int i = 0; i < VAProcDeinterlacingCount - 1; ++i)
+ if (OpenDeinterlace_IsValidType(filter, caps, num_caps,
+ deint_modes + i, true))
+ {
+ *p_deint_mode = deint_modes[i].type;
+ msg_Dbg(filter, "using %s deinterlace method",
+ deint_modes[i].name);
+ return VLC_SUCCESS;
+ }
+
+ /* We shouldn't be able to reach this, 'cause if there is no deinterlacing
+ algorithm available, then the driver would have told us before the
+ deinterlace filtering is not supported at all. */
+
+ msg_Err(filter, "no algorithm available");
+ return VLC_EGENERIC;
+}
+
+static int
+OpenDeinterlace_InitFilterParams(filter_t * filter, void * p_data,
+ void ** pp_va_params,
+ uint32_t * p_va_param_sz,
+ uint32_t * p_num_va_params)
+{ VLC_UNUSED(p_data);
+ filter_sys_t *const filter_sys = filter->p_sys;
+ VAProcDeinterlacingType caps[VAProcDeinterlacingCount];
+ unsigned int num_caps = VAProcDeinterlacingCount;
+
+ if (vlc_vaapi_QueryVideoProcFilterCaps(VLC_OBJECT(filter),
+ filter_sys->va.dpy,
+ filter_sys->va.buf,
+ VAProcFilterDeinterlacing,
+ &caps, &num_caps))
+ return VLC_EGENERIC;
+
+ VAProcDeinterlacingType va_mode;
+ char *const psz_deint_mode =
+ var_InheritString(filter, "deinterlace-mode");
+
+ int ret = OpenDeinterlace_GetMode(filter, psz_deint_mode,
+ &va_mode, caps, num_caps);
+ free(psz_deint_mode);
+ if (ret)
+ return VLC_EGENERIC;
+
+ VAProcFilterParameterBufferDeinterlacing * p_va_param;
+
+ *p_va_param_sz = sizeof(*p_va_param);
+ *p_num_va_params = 1;
+
+ p_va_param = calloc(1, sizeof(*p_va_param));
+ if (!p_va_param)
+ return VLC_ENOMEM;
+
+ p_va_param->type = VAProcFilterDeinterlacing;
+ p_va_param->algorithm = va_mode;
+ *pp_va_params = p_va_param;
+
+ return VLC_SUCCESS;
+}
+
+static int
+OpenDeinterlace_InitHistory(void * p_data, VAProcPipelineCaps const * pipeline_caps)
+{
+ struct deint_data *const p_deint_data = p_data;
+ unsigned int const sz_backward_refs =
+ pipeline_caps->num_backward_references;
+ unsigned int const sz_forward_refs =
+ pipeline_caps->num_forward_references;
+ unsigned int const history_sz =
+ sz_backward_refs + 1 + sz_forward_refs;
+
+ p_deint_data->history.pp_pics = calloc(history_sz, sizeof(picture_t *));
+ if (!p_deint_data->history.pp_pics)
+ return VLC_ENOMEM;
+
+ p_deint_data->history.pp_cur_pic =
+ p_deint_data->history.pp_pics + sz_backward_refs;
+ p_deint_data->history.num_pics = 0;
+ p_deint_data->history.sz = history_sz;
+
+ if (history_sz - 1)
+ {
+ p_deint_data->backward_refs.surfaces =
+ malloc((history_sz - 1) * sizeof(VASurfaceID));
+ if (!p_deint_data->backward_refs.surfaces)
+ return VLC_ENOMEM;
+ }
+
+ p_deint_data->forward_refs.surfaces =
+ p_deint_data->backward_refs.surfaces + sz_backward_refs;
+
+ p_deint_data->backward_refs.sz = sz_backward_refs;
+ p_deint_data->forward_refs.sz = sz_forward_refs;
+
+ return VLC_SUCCESS;
+}
+
+static int
+OpenDeinterlace(vlc_object_t * obj)
+{
+ VAProcPipelineCaps pipeline_caps;
+ filter_t *const filter = (filter_t *)obj;
+ struct deint_data *const p_data = calloc(1, sizeof(*p_data));
+ if (!p_data)
+ return VLC_ENOMEM;
+
+ if (Open(filter, VAProcFilterDeinterlacing, &pipeline_caps, p_data,
+ OpenDeinterlace_InitFilterParams, OpenDeinterlace_InitHistory))
+ goto error;
+
+ filter->pf_video_filter = Deinterlace;
+
+ return VLC_SUCCESS;
+
+error:
+ if (p_data->backward_refs.surfaces)
+ free(p_data->backward_refs.surfaces);
+ if (p_data->history.pp_pics)
+ free(p_data->history.pp_pics);
+ free(p_data);
+ return VLC_EGENERIC;
+}
+
+static void
+CloseDeinterlace(vlc_object_t * obj)
+{
+ filter_t *const filter = (filter_t *)obj;
+ filter_sys_t *const filter_sys = filter->p_sys;
+ struct deint_data *const p_data = filter_sys->p_data;
+
+ if (p_data->backward_refs.surfaces)
+ free(p_data->backward_refs.surfaces);
+ if (p_data->history.pp_pics)
+ {
+ while (p_data->history.num_pics)
+ picture_Release(p_data->history.pp_pics[--p_data->history.num_pics]);
+ free(p_data->history.pp_pics);
+ }
+ free(p_data);
+ Close(obj, filter_sys);
+}
+
+/*********************
+ * Module descriptor *
+ *********************/
+
+vlc_module_begin()
+ set_shortname(N_("VAAPI filters"))
+ set_description(N_("Video Accelerated API filters"))
+ set_category(CAT_VIDEO)
+ set_subcategory(SUBCAT_VIDEO_VFILTER)
+ set_capability("video filter", 0)
+
+ add_submodule()
+ set_callbacks(OpenAdjust, CloseAdjust)
+ add_shortcut("adjust")
+
+ add_submodule()
+ set_callbacks(OpenDeinterlace, CloseDeinterlace)
+ add_shortcut("deinterlace")
+
+ add_submodule()
+ set_callbacks(OpenBasicFilter, CloseBasicFilter)
+ add_float_with_range("denoise-sigma", 1.f, .0f, .0f,
+ N_("Denoise strength (0-2)"),
+ N_("Set the Denoise strength, between 0 and 2. " \
+ "Defaults to 1."),
+ false)
+ add_shortcut("denoise")
+
+ add_submodule()
+ set_callbacks(OpenBasicFilter, CloseBasicFilter)
+ add_shortcut("sharpen")
+vlc_module_end()
--
2.13.1
More information about the vlc-devel
mailing list