Merge branch 'move_subdir_editing-services' into tizen_gst_1.19.2_mono
[platform/upstream/gstreamer.git] / subprojects / gstreamer-vaapi / gst-libs / gst / vaapi / gstvaapifilter.c
1 /*
2  *  gstvaapifilter.c - Video processing abstraction
3  *
4  *  Copyright (C) 2013-2014 Intel Corporation
5  *    Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Lesser General Public License
9  *  as published by the Free Software Foundation; either version 2.1
10  *  of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this library; if not, write to the Free
19  *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  *  Boston, MA 02110-1301 USA
21  */
22
23 #include "sysdeps.h"
24 #include "gstvaapicompat.h"
25 #include "gstvaapifilter.h"
26 #include "gstvaapiutils.h"
27 #include "gstvaapivalue.h"
28 #include "gstvaapiminiobject.h"
29 #include "gstvaapidisplay_priv.h"
30 #include "gstvaapisurface_priv.h"
31 #include "gstvaapiutils_core.h"
32
33 #define GST_VAAPI_FILTER_CAST(obj) \
34     ((GstVaapiFilter *)(obj))
35
36 typedef struct _GstVaapiFilterOpData GstVaapiFilterOpData;
37 struct _GstVaapiFilterOpData
38 {
39   GstVaapiFilterOp op;
40   GParamSpec *pspec;
41   gint ref_count;
42   guint va_type;
43   guint va_subtype;
44   gpointer va_caps;
45   guint va_num_caps;
46   guint va_cap_size;
47   VABufferID va_buffer;
48   guint va_buffer_size;
49   guint is_enabled:1;
50 };
51
52 struct _GstVaapiFilter
53 {
54   /*< private > */
55   GstObject parent_instance;
56
57   GstVaapiDisplay *display;
58   VADisplay va_display;
59   VAConfigID va_config;
60   VAContextID va_context;
61   GPtrArray *operations;
62   GstVideoFormat format;
63   GstVaapiScaleMethod scale_method;
64   GstVideoOrientationMethod video_direction;
65   GstVaapiConfigSurfaceAttributes *attribs;
66   GArray *forward_references;
67   GArray *backward_references;
68   GstVaapiRectangle crop_rect;
69   GstVaapiRectangle target_rect;
70   guint use_crop_rect:1;
71   guint use_target_rect:1;
72   guint32 mirror_flags;
73   guint32 rotation_flags;
74
75   GstVideoColorimetry input_colorimetry;
76   GstVideoColorimetry output_colorimetry;
77
78 #if VA_CHECK_VERSION(1,4,0)
79   VAHdrMetaDataHDR10 hdr_meta;
80 #endif
81 };
82
83 typedef struct _GstVaapiFilterClass GstVaapiFilterClass;
84 struct _GstVaapiFilterClass
85 {
86   /*< private > */
87   GstObjectClass parent_class;
88 };
89
90 /* Debug category for VaapiFilter */
91 GST_DEBUG_CATEGORY (gst_debug_vaapi_filter);
92 #define GST_CAT_DEFAULT gst_debug_vaapi_filter
93
94 #define _do_init                                                       \
95     GST_DEBUG_CATEGORY_INIT (gst_debug_vaapi_filter, "vaapifilter", 0, \
96     "VA-API Filter");
97
98 G_DEFINE_TYPE_WITH_CODE (GstVaapiFilter, gst_vaapi_filter, GST_TYPE_OBJECT,
99     _do_init);
100
101 /* ------------------------------------------------------------------------- */
102 /* --- VPP Types                                                         --- */
103 /* ------------------------------------------------------------------------- */
104
105 static GType
106 gst_vaapi_scale_method_get_type (void)
107 {
108   static gsize g_type = 0;
109
110   static const GEnumValue enum_values[] = {
111     {GST_VAAPI_SCALE_METHOD_DEFAULT,
112         "Default scaling mode", "default"},
113     {GST_VAAPI_SCALE_METHOD_FAST,
114         "Fast scaling mode", "fast"},
115     {GST_VAAPI_SCALE_METHOD_HQ,
116         "High quality scaling mode", "hq"},
117     {0, NULL, NULL},
118   };
119
120   if (g_once_init_enter (&g_type)) {
121     const GType type =
122         g_enum_register_static ("GstVaapiScaleMethod", enum_values);
123     g_once_init_leave (&g_type, type);
124
125     gst_type_mark_as_plugin_api (type, 0);
126   }
127   return g_type;
128 }
129
130 GType
131 gst_vaapi_deinterlace_method_get_type (void)
132 {
133   static gsize g_type = 0;
134
135   static const GEnumValue enum_values[] = {
136     {GST_VAAPI_DEINTERLACE_METHOD_NONE,
137         "Disable deinterlacing", "none"},
138     {GST_VAAPI_DEINTERLACE_METHOD_BOB,
139         "Bob deinterlacing", "bob"},
140     {GST_VAAPI_DEINTERLACE_METHOD_WEAVE,
141         "Weave deinterlacing", "weave"},
142     {GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE,
143         "Motion adaptive deinterlacing", "motion-adaptive"},
144     {GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED,
145         "Motion compensated deinterlacing", "motion-compensated"},
146     {0, NULL, NULL},
147   };
148
149   if (g_once_init_enter (&g_type)) {
150     const GType type =
151         g_enum_register_static ("GstVaapiDeinterlaceMethod", enum_values);
152     gst_type_mark_as_plugin_api (type, 0);
153     g_once_init_leave (&g_type, type);
154   }
155   return g_type;
156 }
157
158 GType
159 gst_vaapi_deinterlace_flags_get_type (void)
160 {
161   static gsize g_type = 0;
162
163   static const GEnumValue enum_values[] = {
164     {GST_VAAPI_DEINTERLACE_FLAG_TFF,
165         "Top-field first", "top-field-first"},
166     {GST_VAAPI_DEINTERLACE_FLAG_ONEFIELD,
167         "One field", "one-field"},
168     {GST_VAAPI_DEINTERLACE_FLAG_TOPFIELD,
169         "Top field", "top-field"},
170     {0, NULL, NULL}
171   };
172
173   if (g_once_init_enter (&g_type)) {
174     const GType type =
175         g_enum_register_static ("GstVaapiDeinterlaceFlags", enum_values);
176     gst_type_mark_as_plugin_api (type, 0);
177     g_once_init_leave (&g_type, type);
178   }
179   return g_type;
180 }
181
182 /* ------------------------------------------------------------------------- */
183 /* --- VPP Helpers                                                       --- */
184 /* ------------------------------------------------------------------------- */
185
186 static VAProcFilterType *
187 vpp_get_filters_unlocked (GstVaapiFilter * filter, guint * num_filters_ptr)
188 {
189   VAProcFilterType *filters = NULL;
190   guint num_filters = 0;
191   VAStatus va_status;
192
193   num_filters = VAProcFilterCount;
194   filters = g_malloc_n (num_filters, sizeof (*filters));
195   if (!filters)
196     goto error;
197
198   va_status = vaQueryVideoProcFilters (filter->va_display, filter->va_context,
199       filters, &num_filters);
200
201   // Try to reallocate to the expected number of filters
202   if (va_status == VA_STATUS_ERROR_MAX_NUM_EXCEEDED) {
203     VAProcFilterType *const new_filters =
204         g_try_realloc_n (filters, num_filters, sizeof (*new_filters));
205     if (!new_filters)
206       goto error;
207     filters = new_filters;
208
209     va_status = vaQueryVideoProcFilters (filter->va_display,
210         filter->va_context, filters, &num_filters);
211   }
212   if (!vaapi_check_status (va_status, "vaQueryVideoProcFilters()"))
213     goto error;
214
215   *num_filters_ptr = num_filters;
216   return filters;
217
218   /* ERRORS */
219 error:
220   {
221     g_free (filters);
222     return NULL;
223   }
224 }
225
226 static VAProcFilterType *
227 vpp_get_filters (GstVaapiFilter * filter, guint * num_filters_ptr)
228 {
229   VAProcFilterType *filters;
230
231   GST_VAAPI_DISPLAY_LOCK (filter->display);
232   filters = vpp_get_filters_unlocked (filter, num_filters_ptr);
233   GST_VAAPI_DISPLAY_UNLOCK (filter->display);
234   return filters;
235 }
236
237 static gpointer
238 vpp_get_filter_caps_unlocked (GstVaapiFilter * filter, VAProcFilterType type,
239     guint cap_size, guint * num_caps_ptr)
240 {
241   gpointer caps;
242   guint num_caps = 1;
243   VAStatus va_status;
244
245   caps = g_malloc (cap_size);
246   if (!caps)
247     goto error;
248
249   va_status = vaQueryVideoProcFilterCaps (filter->va_display,
250       filter->va_context, type, caps, &num_caps);
251
252   // Try to reallocate to the expected number of filters
253   if (va_status == VA_STATUS_ERROR_MAX_NUM_EXCEEDED) {
254     gpointer const new_caps = g_try_realloc_n (caps, num_caps, cap_size);
255     if (!new_caps)
256       goto error;
257     caps = new_caps;
258
259     va_status = vaQueryVideoProcFilterCaps (filter->va_display,
260         filter->va_context, type, caps, &num_caps);
261   }
262   if (!vaapi_check_status (va_status, "vaQueryVideoProcFilterCaps()"))
263     goto error;
264
265   *num_caps_ptr = num_caps;
266   return caps;
267
268   /* ERRORS */
269 error:
270   {
271     g_free (caps);
272     return NULL;
273   }
274 }
275
276 static gpointer
277 vpp_get_filter_caps (GstVaapiFilter * filter, VAProcFilterType type,
278     guint cap_size, guint * num_caps_ptr)
279 {
280   gpointer caps;
281
282   GST_VAAPI_DISPLAY_LOCK (filter->display);
283   caps = vpp_get_filter_caps_unlocked (filter, type, cap_size, num_caps_ptr);
284   GST_VAAPI_DISPLAY_UNLOCK (filter->display);
285   return caps;
286 }
287
288 static void
289 vpp_get_pipeline_caps_unlocked (GstVaapiFilter * filter)
290 {
291 #if VA_CHECK_VERSION(1,1,0)
292   VAProcPipelineCaps pipeline_caps = { 0, };
293
294   VAStatus va_status = vaQueryVideoProcPipelineCaps (filter->va_display,
295       filter->va_context, NULL, 0, &pipeline_caps);
296
297   if (vaapi_check_status (va_status, "vaQueryVideoProcPipelineCaps()")) {
298     filter->mirror_flags = pipeline_caps.mirror_flags;
299     filter->rotation_flags = pipeline_caps.rotation_flags;
300     return;
301   }
302 #endif
303
304   filter->mirror_flags = 0;
305   filter->rotation_flags = 0;
306 }
307
308 static void
309 vpp_get_pipeline_caps (GstVaapiFilter * filter)
310 {
311   GST_VAAPI_DISPLAY_LOCK (filter->display);
312   vpp_get_pipeline_caps_unlocked (filter);
313   GST_VAAPI_DISPLAY_UNLOCK (filter->display);
314 }
315
316 /* ------------------------------------------------------------------------- */
317 /* --- VPP Operations                                                   --- */
318 /* ------------------------------------------------------------------------- */
319
320 #define DEFAULT_FORMAT  GST_VIDEO_FORMAT_UNKNOWN
321
322 #define OP_DATA_DEFAULT_VALUE(type, op_data) \
323     g_value_get_##type (g_param_spec_get_default_value (op_data->pspec))
324
325 #define OP_RET_DEFAULT_VALUE(type, filter, op) \
326     do { \
327       g_return_val_if_fail (filter != NULL, FALSE); \
328       return OP_DATA_DEFAULT_VALUE (type, find_operation (filter, op)); \
329     } while (0)
330
331 enum
332 {
333   PROP_DISPLAY = 1,
334 };
335
336 enum
337 {
338   PROP_0,
339
340   PROP_FORMAT = GST_VAAPI_FILTER_OP_FORMAT,
341   PROP_CROP = GST_VAAPI_FILTER_OP_CROP,
342   PROP_DENOISE = GST_VAAPI_FILTER_OP_DENOISE,
343   PROP_SHARPEN = GST_VAAPI_FILTER_OP_SHARPEN,
344   PROP_HUE = GST_VAAPI_FILTER_OP_HUE,
345   PROP_SATURATION = GST_VAAPI_FILTER_OP_SATURATION,
346   PROP_BRIGHTNESS = GST_VAAPI_FILTER_OP_BRIGHTNESS,
347   PROP_CONTRAST = GST_VAAPI_FILTER_OP_CONTRAST,
348   PROP_DEINTERLACING = GST_VAAPI_FILTER_OP_DEINTERLACING,
349   PROP_SCALING = GST_VAAPI_FILTER_OP_SCALING,
350   PROP_VIDEO_DIRECTION = GST_VAAPI_FILTER_OP_VIDEO_DIRECTION,
351   PROP_HDR_TONE_MAP = GST_VAAPI_FILTER_OP_HDR_TONE_MAP,
352 #ifndef GST_REMOVE_DEPRECATED
353   PROP_SKINTONE = GST_VAAPI_FILTER_OP_SKINTONE,
354 #endif
355   PROP_SKINTONE_LEVEL = GST_VAAPI_FILTER_OP_SKINTONE_LEVEL,
356
357   N_PROPERTIES
358 };
359
360 static GParamSpec *g_properties[N_PROPERTIES] = { NULL, };
361
362 static gsize g_properties_initialized = FALSE;
363
364 static void
365 init_properties (void)
366 {
367   /**
368    * GstVaapiFilter:format:
369    *
370    * The forced output pixel format, expressed as a #GstVideoFormat.
371    */
372   g_properties[PROP_FORMAT] = g_param_spec_enum ("format",
373       "Format",
374       "The forced output pixel format",
375       GST_TYPE_VIDEO_FORMAT,
376       DEFAULT_FORMAT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
377
378   /**
379    * GstVaapiFilter:crop-rect:
380    *
381    * The cropping rectangle, expressed as a #GstVaapiRectangle.
382    */
383   g_properties[PROP_CROP] = g_param_spec_boxed ("crop-rect",
384       "Cropping Rectangle",
385       "The cropping rectangle",
386       GST_VAAPI_TYPE_RECTANGLE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
387
388   /**
389    * GstVaapiFilter:denoise:
390    *
391    * The level of noise reduction to apply.
392    */
393   g_properties[PROP_DENOISE] = g_param_spec_float ("denoise",
394       "Denoising Level",
395       "The level of denoising to apply",
396       0.0, 1.0, 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
397
398   /**
399    * GstVaapiFilter:sharpen:
400    *
401    * The level of sharpening to apply for positive values, or the
402    * level of blurring for negative values.
403    */
404   g_properties[PROP_SHARPEN] = g_param_spec_float ("sharpen",
405       "Sharpening Level",
406       "The level of sharpening/blurring to apply",
407       -1.0, 1.0, 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
408
409   /**
410    * GstVaapiFilter:hue:
411    *
412    * The color hue, expressed as a float value. Range is -180.0 to
413    * 180.0. Default value is 0.0 and represents no modification.
414    */
415   g_properties[PROP_HUE] = g_param_spec_float ("hue",
416       "Hue",
417       "The color hue value",
418       -180.0, 180.0, 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
419
420   /**
421    * GstVaapiFilter:saturation:
422    *
423    * The color saturation, expressed as a float value. Range is 0.0 to
424    * 2.0. Default value is 1.0 and represents no modification.
425    */
426   g_properties[PROP_SATURATION] = g_param_spec_float ("saturation",
427       "Saturation",
428       "The color saturation value",
429       0.0, 2.0, 1.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
430
431   /**
432    * GstVaapiFilter:brightness:
433    *
434    * The color brightness, expressed as a float value. Range is -1.0
435    * to 1.0. Default value is 0.0 and represents no modification.
436    */
437   g_properties[PROP_BRIGHTNESS] = g_param_spec_float ("brightness",
438       "Brightness",
439       "The color brightness value",
440       -1.0, 1.0, 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
441
442   /**
443    * GstVaapiFilter:contrast:
444    *
445    * The color contrast, expressed as a float value. Range is 0.0 to
446    * 2.0. Default value is 1.0 and represents no modification.
447    */
448   g_properties[PROP_CONTRAST] = g_param_spec_float ("contrast",
449       "Contrast",
450       "The color contrast value",
451       0.0, 2.0, 1.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
452
453   /**
454    * GstVaapiFilter:deinterlace-method:
455    *
456    * The deinterlacing algorithm to apply, expressed a an enum
457    * value. See #GstVaapiDeinterlaceMethod.
458    */
459   g_properties[PROP_DEINTERLACING] = g_param_spec_enum ("deinterlace",
460       "Deinterlacing Method",
461       "Deinterlacing method to apply",
462       GST_VAAPI_TYPE_DEINTERLACE_METHOD,
463       GST_VAAPI_DEINTERLACE_METHOD_NONE,
464       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
465
466   /**
467    * GstVaapiFilter:scale-method:
468    *
469    * The scaling method to use, expressed as an enum value. See
470    * #GstVaapiScaleMethod.
471    */
472   g_properties[PROP_SCALING] = g_param_spec_enum ("scale-method",
473       "Scaling Method",
474       "Scaling method to use",
475       GST_VAAPI_TYPE_SCALE_METHOD,
476       GST_VAAPI_SCALE_METHOD_DEFAULT,
477       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
478
479   /**
480    * GstVaapiFilter:video-direction:
481    *
482    * The video-direction to use, expressed as an enum value. See
483    * #GstVideoOrientationMethod.
484    */
485   g_properties[PROP_VIDEO_DIRECTION] = g_param_spec_enum ("video-direction",
486       "Video Direction",
487       "Video direction: rotation and flipping",
488       GST_TYPE_VIDEO_ORIENTATION_METHOD,
489       GST_VIDEO_ORIENTATION_IDENTITY,
490       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
491
492   /**
493    * GstVaapiFilter:tone-map:
494    *
495    * Apply HDR tone mapping
496    **/
497   g_properties[PROP_HDR_TONE_MAP] = g_param_spec_boolean ("hdr-tone-map",
498       "HDR Tone Mapping",
499       "Apply HDR tone mapping",
500       FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
501
502 #ifndef GST_REMOVE_DEPRECATED
503   /**
504    * GstVaapiFilter:skin-tone-enhancement:
505    *
506    * Apply the skin tone enhancement algorithm.
507    */
508   g_properties[PROP_SKINTONE] = g_param_spec_boolean ("skin-tone-enhancement",
509       "Skin tone enhancement",
510       "Apply the skin tone enhancement algorithm",
511       FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
512 #endif
513
514   /**
515    * GstVaapiFilter:skin-tone-enhancement-level:
516    *
517    * Apply the skin tone enhancement algorithm with specified value.
518    */
519   g_properties[PROP_SKINTONE_LEVEL] =
520       g_param_spec_uint ("skin-tone-enhancement-level",
521       "Skin tone enhancement level",
522       "Apply the skin tone enhancement algorithm with specified level", 0, 9, 3,
523       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
524 }
525
526 static void
527 ensure_properties (void)
528 {
529   if (g_once_init_enter (&g_properties_initialized)) {
530     init_properties ();
531     g_once_init_leave (&g_properties_initialized, TRUE);
532   }
533 }
534
535 static void
536 op_data_free (GstVaapiFilterOpData * op_data)
537 {
538   g_free (op_data->va_caps);
539   g_slice_free (GstVaapiFilterOpData, op_data);
540 }
541
542 static inline gpointer
543 op_data_new (GstVaapiFilterOp op, GParamSpec * pspec)
544 {
545   GstVaapiFilterOpData *op_data;
546
547   op_data = g_slice_new0 (GstVaapiFilterOpData);
548   if (!op_data)
549     return NULL;
550
551   op_data->op = op;
552   op_data->pspec = pspec;
553   g_atomic_int_set (&op_data->ref_count, 1);
554   op_data->va_buffer = VA_INVALID_ID;
555
556   switch (op) {
557     case GST_VAAPI_FILTER_OP_HDR_TONE_MAP:
558 #if VA_CHECK_VERSION(1,4,0)
559       /* Only HDR10 tone mapping is supported */
560       op_data->va_type = VAProcFilterHighDynamicRangeToneMapping;
561       op_data->va_subtype = VAProcHighDynamicRangeMetadataHDR10;
562       op_data->va_cap_size = sizeof (VAProcFilterCapHighDynamicRange);
563       op_data->va_buffer_size =
564           sizeof (VAProcFilterParameterBufferHDRToneMapping);
565       break;
566 #else
567       /* fall-through */
568 #endif
569     case GST_VAAPI_FILTER_OP_FORMAT:
570     case GST_VAAPI_FILTER_OP_CROP:
571     case GST_VAAPI_FILTER_OP_SCALING:
572     case GST_VAAPI_FILTER_OP_VIDEO_DIRECTION:
573       op_data->va_type = VAProcFilterNone;
574       break;
575     case GST_VAAPI_FILTER_OP_DENOISE:
576       op_data->va_type = VAProcFilterNoiseReduction;
577       op_data->va_cap_size = sizeof (VAProcFilterCap);
578       op_data->va_buffer_size = sizeof (VAProcFilterParameterBuffer);
579       break;
580     case GST_VAAPI_FILTER_OP_SHARPEN:
581       op_data->va_type = VAProcFilterSharpening;
582       op_data->va_cap_size = sizeof (VAProcFilterCap);
583       op_data->va_buffer_size = sizeof (VAProcFilterParameterBuffer);
584       break;
585 #ifndef GST_REMOVE_DEPRECATED
586     case GST_VAAPI_FILTER_OP_SKINTONE:
587 #endif
588     case GST_VAAPI_FILTER_OP_SKINTONE_LEVEL:
589       op_data->va_type = VAProcFilterSkinToneEnhancement;
590       op_data->va_buffer_size = sizeof (VAProcFilterParameterBuffer);
591       break;
592     case GST_VAAPI_FILTER_OP_HUE:
593       op_data->va_subtype = VAProcColorBalanceHue;
594       goto op_colorbalance;
595     case GST_VAAPI_FILTER_OP_SATURATION:
596       op_data->va_subtype = VAProcColorBalanceSaturation;
597       goto op_colorbalance;
598     case GST_VAAPI_FILTER_OP_BRIGHTNESS:
599       op_data->va_subtype = VAProcColorBalanceBrightness;
600       goto op_colorbalance;
601     case GST_VAAPI_FILTER_OP_CONTRAST:
602       op_data->va_subtype = VAProcColorBalanceContrast;
603     op_colorbalance:
604       op_data->va_type = VAProcFilterColorBalance;
605       op_data->va_cap_size = sizeof (VAProcFilterCapColorBalance);
606       op_data->va_buffer_size =
607           sizeof (VAProcFilterParameterBufferColorBalance);
608       break;
609     case GST_VAAPI_FILTER_OP_DEINTERLACING:
610       op_data->va_type = VAProcFilterDeinterlacing;
611       op_data->va_cap_size = sizeof (VAProcFilterCapDeinterlacing);
612       op_data->va_buffer_size =
613           sizeof (VAProcFilterParameterBufferDeinterlacing);
614       break;
615     default:
616       g_assert (0 && "unsupported operation");
617       goto error;
618   }
619   return op_data;
620
621   /* ERRORS */
622 error:
623   {
624     op_data_free (op_data);
625     return NULL;
626   }
627 }
628
629 static inline gpointer
630 op_data_ref (gpointer data)
631 {
632   GstVaapiFilterOpData *const op_data = data;
633
634   g_return_val_if_fail (op_data != NULL, NULL);
635
636   g_atomic_int_inc (&op_data->ref_count);
637   return op_data;
638 }
639
640 static void
641 op_data_unref (gpointer data)
642 {
643   GstVaapiFilterOpData *const op_data = data;
644
645   g_return_if_fail (op_data != NULL);
646   g_return_if_fail (op_data->ref_count > 0);
647
648   if (g_atomic_int_dec_and_test (&op_data->ref_count))
649     op_data_free (op_data);
650 }
651
652 /* Ensure capability info is set up for the VA filter we are interested in */
653 static gboolean
654 op_data_ensure_caps (GstVaapiFilterOpData * op_data, gpointer filter_caps,
655     guint num_filter_caps)
656 {
657   guchar *filter_cap = filter_caps;
658   guint i, va_num_caps = num_filter_caps;
659
660   // Find the VA filter cap matching the op info sub-type
661   if (op_data->va_subtype) {
662     for (i = 0; i < num_filter_caps; i++) {
663       /* XXX: sub-type shall always be the first field */
664       if (op_data->va_subtype == *(guint *) filter_cap) {
665         va_num_caps = 1;
666         break;
667       }
668       filter_cap += op_data->va_cap_size;
669     }
670     if (i == num_filter_caps)
671       return FALSE;
672   }
673
674   op_data->va_caps = g_memdup2 (filter_cap, op_data->va_cap_size * va_num_caps);
675   if (!op_data->va_caps)
676     return FALSE;
677
678   op_data->va_num_caps = va_num_caps;
679   return TRUE;
680 }
681
682 /* Scale the filter value wrt. library spec and VA driver spec */
683 static gboolean
684 op_data_get_value_float (GstVaapiFilterOpData * op_data,
685     const VAProcFilterValueRange * range, gfloat value, gfloat * out_value_ptr)
686 {
687   GParamSpecFloat *const pspec = G_PARAM_SPEC_FLOAT (op_data->pspec);
688   gfloat out_value;
689
690   g_return_val_if_fail (range != NULL, FALSE);
691   g_return_val_if_fail (out_value_ptr != NULL, FALSE);
692
693   if (value < pspec->minimum || value > pspec->maximum)
694     return FALSE;
695
696   // Scale wrt. the medium ("default") value
697   out_value = range->default_value;
698   if (value > pspec->default_value)
699     out_value += ((value - pspec->default_value) /
700         (pspec->maximum - pspec->default_value) *
701         (range->max_value - range->default_value));
702   else if (value < pspec->default_value)
703     out_value -= ((pspec->default_value - value) /
704         (pspec->default_value - pspec->minimum) *
705         (range->default_value - range->min_value));
706
707   *out_value_ptr = out_value;
708   return TRUE;
709 }
710
711 /* Get default list of operations supported by the library */
712 static GPtrArray *
713 get_operations_default (void)
714 {
715   GPtrArray *ops;
716   guint i;
717
718   ops = g_ptr_array_new_full (N_PROPERTIES, op_data_unref);
719   if (!ops)
720     return NULL;
721
722   ensure_properties ();
723
724   for (i = 0; i < N_PROPERTIES; i++) {
725     GstVaapiFilterOpData *op_data;
726     GParamSpec *const pspec = g_properties[i];
727     if (!pspec)
728       continue;
729
730     op_data = op_data_new (i, pspec);
731     if (!op_data)
732       goto error;
733     g_ptr_array_add (ops, op_data);
734   }
735   return ops;
736
737   /* ERRORS */
738 error:
739   {
740     g_ptr_array_unref (ops);
741     return NULL;
742   }
743 }
744
745 /* Get the ordered list of operations, based on VA/VPP queries */
746 static GPtrArray *
747 get_operations_ordered (GstVaapiFilter * filter, GPtrArray * default_ops)
748 {
749   GPtrArray *ops;
750   VAProcFilterType *filters;
751   gpointer filter_caps = NULL;
752   guint i, j, num_filters, num_filter_caps = 0;
753
754   ops = g_ptr_array_new_full (default_ops->len, op_data_unref);
755   if (!ops)
756     return NULL;
757
758   filters = vpp_get_filters (filter, &num_filters);
759   if (!filters)
760     goto error;
761
762   // Append virtual ops first, i.e. those without an associated VA filter
763   for (i = 0; i < default_ops->len; i++) {
764     GstVaapiFilterOpData *const op_data = g_ptr_array_index (default_ops, i);
765     if (op_data->va_type == VAProcFilterNone)
766       g_ptr_array_add (ops, op_data_ref (op_data));
767   }
768
769   // Append ops, while preserving the VA filters ordering
770   for (i = 0; i < num_filters; i++) {
771     const VAProcFilterType va_type = filters[i];
772     if (va_type == VAProcFilterNone)
773       continue;
774
775     for (j = 0; j < default_ops->len; j++) {
776       GstVaapiFilterOpData *const op_data = g_ptr_array_index (default_ops, j);
777       if (op_data->va_type != va_type)
778         continue;
779
780       if (op_data->va_cap_size == 0) {  /* no caps, like skintone */
781         g_ptr_array_add (ops, op_data_ref (op_data));
782         continue;
783       }
784
785       if (!filter_caps) {
786         filter_caps = vpp_get_filter_caps (filter, va_type,
787             op_data->va_cap_size, &num_filter_caps);
788         if (!filter_caps)
789           goto error;
790       }
791       if (!op_data_ensure_caps (op_data, filter_caps, num_filter_caps))
792         goto error;
793       g_ptr_array_add (ops, op_data_ref (op_data));
794     }
795     free (filter_caps);
796     filter_caps = NULL;
797   }
798
799   vpp_get_pipeline_caps (filter);
800
801   if (filter->operations)
802     g_ptr_array_unref (filter->operations);
803   filter->operations = g_ptr_array_ref (ops);
804
805   g_free (filters);
806   g_ptr_array_unref (default_ops);
807   return ops;
808
809   /* ERRORS */
810 error:
811   {
812     g_free (filter_caps);
813     g_free (filters);
814     g_ptr_array_unref (ops);
815     g_ptr_array_unref (default_ops);
816     return NULL;
817   }
818 }
819
820 /* Determine the set of supported VPP operations by the specific
821    filter, or known to this library if filter is NULL */
822 static GPtrArray *
823 get_operations (GstVaapiFilter * filter)
824 {
825   GPtrArray *ops;
826
827   if (filter && filter->operations)
828     return g_ptr_array_ref (filter->operations);
829
830   ops = get_operations_default ();
831   if (!ops)
832     return NULL;
833   return filter ? get_operations_ordered (filter, ops) : ops;
834 }
835
836 /* Ensure the set of supported VPP operations is cached into the
837    GstVaapiFilter::operations member */
838 static inline gboolean
839 ensure_operations (GstVaapiFilter * filter)
840 {
841   GPtrArray *ops;
842
843   if (!filter)
844     return FALSE;
845
846   if (filter->operations)
847     return TRUE;
848
849   ops = get_operations (filter);
850   if (!ops)
851     return FALSE;
852
853   g_ptr_array_unref (ops);
854   return TRUE;
855 }
856
857 /* Find whether the VPP operation is supported or not */
858 static GstVaapiFilterOpData *
859 find_operation (GstVaapiFilter * filter, GstVaapiFilterOp op)
860 {
861   guint i;
862
863   if (!ensure_operations (filter))
864     return NULL;
865
866   for (i = 0; i < filter->operations->len; i++) {
867     GstVaapiFilterOpData *const op_data =
868         g_ptr_array_index (filter->operations, i);
869     if (op_data->op == op)
870       return op_data;
871   }
872   return NULL;
873 }
874
875 /* Ensure the operation's VA buffer is allocated */
876 static inline gboolean
877 op_ensure_n_elements_buffer (GstVaapiFilter * filter,
878     GstVaapiFilterOpData * op_data, gint op_num)
879 {
880   if (G_LIKELY (op_data->va_buffer != VA_INVALID_ID))
881     return TRUE;
882   return vaapi_create_n_elements_buffer (filter->va_display, filter->va_context,
883       VAProcFilterParameterBufferType, op_data->va_buffer_size, NULL,
884       &op_data->va_buffer, NULL, op_num);
885 }
886
887 static inline gboolean
888 op_ensure_buffer (GstVaapiFilter * filter, GstVaapiFilterOpData * op_data)
889 {
890   return op_ensure_n_elements_buffer (filter, op_data, 1);
891 }
892
893 /* Update a generic filter (float value) */
894 static gboolean
895 op_set_generic_unlocked (GstVaapiFilter * filter,
896     GstVaapiFilterOpData * op_data, gfloat value)
897 {
898   VAProcFilterParameterBuffer *buf;
899   VAProcFilterCap *filter_cap;
900   gfloat va_value;
901
902   if (!op_data || !op_ensure_buffer (filter, op_data))
903     return FALSE;
904
905   op_data->is_enabled = (value != OP_DATA_DEFAULT_VALUE (float, op_data));
906   if (!op_data->is_enabled)
907     return TRUE;
908
909   filter_cap = op_data->va_caps;
910   if (!op_data_get_value_float (op_data, &filter_cap->range, value, &va_value))
911     return FALSE;
912
913   buf = vaapi_map_buffer (filter->va_display, op_data->va_buffer);
914   if (!buf)
915     return FALSE;
916
917   buf->type = op_data->va_type;
918   buf->value = va_value;
919   vaapi_unmap_buffer (filter->va_display, op_data->va_buffer, NULL);
920   return TRUE;
921 }
922
923 static inline gboolean
924 op_set_generic (GstVaapiFilter * filter, GstVaapiFilterOpData * op_data,
925     gfloat value)
926 {
927   gboolean success = FALSE;
928
929   GST_VAAPI_DISPLAY_LOCK (filter->display);
930   success = op_set_generic_unlocked (filter, op_data, value);
931   GST_VAAPI_DISPLAY_UNLOCK (filter->display);
932   return success;
933 }
934
935 /* Update the color balance filter */
936 #define COLOR_BALANCE_NUM \
937     GST_VAAPI_FILTER_OP_CONTRAST - GST_VAAPI_FILTER_OP_HUE + 1
938
939 static gboolean
940 op_set_color_balance_unlocked (GstVaapiFilter * filter,
941     GstVaapiFilterOpData * op_data, gfloat value)
942 {
943   VAProcFilterParameterBufferColorBalance *buf;
944   VAProcFilterCapColorBalance *filter_cap;
945   gfloat va_value;
946   gint i;
947   GstVaapiFilterOpData *color_data[COLOR_BALANCE_NUM];
948   GstVaapiFilterOpData *enabled_data = NULL;
949   gboolean ret = TRUE;
950
951   if (!op_data)
952     return FALSE;
953
954   /* collect all the Color Balance operators and find the first
955    * enabled one */
956   for (i = 0; i < COLOR_BALANCE_NUM; i++) {
957     color_data[i] = find_operation (filter, GST_VAAPI_FILTER_OP_HUE + i);
958     if (!color_data[i])
959       return FALSE;
960
961     if (!enabled_data && color_data[i]->is_enabled)
962       enabled_data = color_data[i];
963   }
964
965   /* If there's no enabled operators let's enable this one.
966    *
967    * HACK: This operator will be the only one with an allocated buffer
968    * which will store all the color balance operators.
969    */
970   if (!enabled_data) {
971     /* *INDENT-OFF* */
972     if (value == OP_DATA_DEFAULT_VALUE (float, op_data))
973       return TRUE;
974     /* *INDENT-ON* */
975
976     if (!op_ensure_n_elements_buffer (filter, op_data, COLOR_BALANCE_NUM))
977       return FALSE;
978
979     enabled_data = op_data;
980
981     buf = vaapi_map_buffer (filter->va_display, enabled_data->va_buffer);
982     if (!buf)
983       return FALSE;
984
985     /* Write all the color balance operator values in the buffer. --
986      * Use the default value for all the operators except the set
987      * one. */
988     for (i = 0; i < COLOR_BALANCE_NUM; i++) {
989       buf[i].type = color_data[i]->va_type;
990       buf[i].attrib = color_data[i]->va_subtype;
991
992       va_value = OP_DATA_DEFAULT_VALUE (float, color_data[i]);
993       if (color_data[i]->op == op_data->op) {
994         filter_cap = color_data[i]->va_caps;
995         /* fail but ignore current value and set default one */
996         if (!op_data_get_value_float (color_data[i], &filter_cap->range, value,
997                 &va_value))
998           ret = FALSE;
999       }
1000
1001       buf[i].value = va_value;
1002     }
1003
1004     enabled_data->is_enabled = 1;
1005   } else {
1006     /* There's already one operator enabled, *in theory* with a
1007      * buffer associated. */
1008     if (G_UNLIKELY (enabled_data->va_buffer == VA_INVALID_ID))
1009       return FALSE;
1010
1011     filter_cap = op_data->va_caps;
1012     if (!op_data_get_value_float (op_data, &filter_cap->range, value,
1013             &va_value))
1014       return FALSE;
1015
1016     buf = vaapi_map_buffer (filter->va_display, enabled_data->va_buffer);
1017     if (!buf)
1018       return FALSE;
1019
1020     buf[op_data->op - GST_VAAPI_FILTER_OP_HUE].value = va_value;
1021   }
1022
1023   vaapi_unmap_buffer (filter->va_display, enabled_data->va_buffer, NULL);
1024
1025   return ret;
1026 }
1027
1028 static inline gboolean
1029 op_set_color_balance (GstVaapiFilter * filter, GstVaapiFilterOpData * op_data,
1030     gfloat value)
1031 {
1032   gboolean success = FALSE;
1033
1034   GST_VAAPI_DISPLAY_LOCK (filter->display);
1035   success = op_set_color_balance_unlocked (filter, op_data, value);
1036   GST_VAAPI_DISPLAY_UNLOCK (filter->display);
1037   return success;
1038 }
1039
1040 /* Update deinterlace filter */
1041 static gboolean
1042 op_set_deinterlace_unlocked (GstVaapiFilter * filter,
1043     GstVaapiFilterOpData * op_data, GstVaapiDeinterlaceMethod method,
1044     guint flags)
1045 {
1046   VAProcFilterParameterBufferDeinterlacing *buf;
1047   const VAProcFilterCapDeinterlacing *filter_caps;
1048   VAProcDeinterlacingType algorithm;
1049   guint i;
1050
1051   if (!op_data || !op_ensure_buffer (filter, op_data))
1052     return FALSE;
1053
1054   op_data->is_enabled = (method != GST_VAAPI_DEINTERLACE_METHOD_NONE);
1055   if (!op_data->is_enabled)
1056     return TRUE;
1057
1058   algorithm = from_GstVaapiDeinterlaceMethod (method);
1059   for (i = 0, filter_caps = op_data->va_caps; i < op_data->va_num_caps; i++) {
1060     if (filter_caps[i].type == algorithm)
1061       break;
1062   }
1063   if (i == op_data->va_num_caps)
1064     return FALSE;
1065
1066   buf = vaapi_map_buffer (filter->va_display, op_data->va_buffer);
1067   if (!buf)
1068     return FALSE;
1069
1070   buf->type = op_data->va_type;
1071   buf->algorithm = algorithm;
1072   buf->flags = from_GstVaapiDeinterlaceFlags (flags);
1073   vaapi_unmap_buffer (filter->va_display, op_data->va_buffer, NULL);
1074   return TRUE;
1075 }
1076
1077 static inline gboolean
1078 op_set_deinterlace (GstVaapiFilter * filter, GstVaapiFilterOpData * op_data,
1079     GstVaapiDeinterlaceMethod method, guint flags)
1080 {
1081   gboolean success = FALSE;
1082
1083   GST_VAAPI_DISPLAY_LOCK (filter->display);
1084   success = op_set_deinterlace_unlocked (filter, op_data, method, flags);
1085   GST_VAAPI_DISPLAY_UNLOCK (filter->display);
1086   return success;
1087 }
1088
1089 /* Update skin tone enhancement level */
1090 static gboolean
1091 op_set_skintone_level_unlocked (GstVaapiFilter * filter,
1092     GstVaapiFilterOpData * op_data, guint value)
1093 {
1094   VAProcFilterParameterBuffer *buf;
1095
1096   if (!op_data || !op_ensure_buffer (filter, op_data))
1097     return FALSE;
1098
1099   op_data->is_enabled = 1;
1100
1101   buf = vaapi_map_buffer (filter->va_display, op_data->va_buffer);
1102   if (!buf)
1103     return FALSE;
1104   buf->type = op_data->va_type;
1105   buf->value = value;
1106   vaapi_unmap_buffer (filter->va_display, op_data->va_buffer, NULL);
1107   return TRUE;
1108 }
1109
1110 static inline gboolean
1111 op_set_skintone_level (GstVaapiFilter * filter,
1112     GstVaapiFilterOpData * op_data, guint value)
1113 {
1114   gboolean success = FALSE;
1115
1116   GST_VAAPI_DISPLAY_LOCK (filter->display);
1117   success = op_set_skintone_level_unlocked (filter, op_data, value);
1118   GST_VAAPI_DISPLAY_UNLOCK (filter->display);
1119   return success;
1120 }
1121
1122 #ifndef GST_REMOVE_DEPRECATED
1123 /* Update skin tone enhancement */
1124 static gboolean
1125 op_set_skintone_unlocked (GstVaapiFilter * filter,
1126     GstVaapiFilterOpData * op_data, gboolean value)
1127 {
1128   if (!op_data)
1129     return FALSE;
1130
1131   if (!value) {
1132     op_data->is_enabled = 0;
1133     return TRUE;
1134   }
1135
1136   return op_set_skintone_level_unlocked (filter, op_data, 3);
1137 }
1138
1139 static inline gboolean
1140 op_set_skintone (GstVaapiFilter * filter, GstVaapiFilterOpData * op_data,
1141     gboolean enhance)
1142 {
1143   gboolean success = FALSE;
1144
1145   GST_VAAPI_DISPLAY_LOCK (filter->display);
1146   success = op_set_skintone_unlocked (filter, op_data, enhance);
1147   GST_VAAPI_DISPLAY_UNLOCK (filter->display);
1148   return success;
1149 }
1150 #endif
1151
1152 static gboolean
1153 op_set_hdr_tone_map_unlocked (GstVaapiFilter * filter,
1154     GstVaapiFilterOpData * op_data, gboolean value)
1155 {
1156 #if VA_CHECK_VERSION(1,4,0)
1157   const VAProcFilterCapHighDynamicRange *filter_caps;
1158   guint i;
1159
1160   if (!op_data)
1161     return !value;
1162
1163   if (!value) {
1164     op_data->is_enabled = 0;
1165     return TRUE;
1166   }
1167
1168   if (!op_ensure_buffer (filter, op_data))
1169     return FALSE;
1170
1171   for (i = 0, filter_caps = op_data->va_caps; i < op_data->va_num_caps; i++) {
1172     if (filter_caps[i].metadata_type == op_data->va_subtype &&
1173         (filter_caps[i].caps_flag & VA_TONE_MAPPING_HDR_TO_SDR))
1174       break;
1175   }
1176   if (i == op_data->va_num_caps)
1177     return FALSE;
1178
1179   op_data->is_enabled = 1;
1180
1181   return TRUE;
1182 #else
1183   return !value;
1184 #endif
1185 }
1186
1187 static inline gboolean
1188 op_set_hdr_tone_map (GstVaapiFilter * filter, GstVaapiFilterOpData * op_data,
1189     gboolean value)
1190 {
1191   gboolean success = FALSE;
1192   GST_VAAPI_DISPLAY_LOCK (filter->display);
1193   success = op_set_hdr_tone_map_unlocked (filter, op_data, value);
1194   GST_VAAPI_DISPLAY_UNLOCK (filter->display);
1195
1196   return success;
1197 }
1198
1199 static gboolean
1200 deint_refs_set (GArray * refs, GstVaapiSurface ** surfaces, guint num_surfaces)
1201 {
1202   guint i;
1203
1204   if (num_surfaces > 0 && !surfaces)
1205     return FALSE;
1206
1207   for (i = 0; i < num_surfaces; i++)
1208     g_array_append_val (refs, GST_VAAPI_SURFACE_ID (surfaces[i]));
1209   return TRUE;
1210 }
1211
1212 static void
1213 deint_refs_clear (GArray * refs)
1214 {
1215   if (refs->len > 0)
1216     g_array_remove_range (refs, 0, refs->len);
1217 }
1218
1219 static inline void
1220 deint_refs_clear_all (GstVaapiFilter * filter)
1221 {
1222   deint_refs_clear (filter->forward_references);
1223   deint_refs_clear (filter->backward_references);
1224 }
1225
1226 /* ------------------------------------------------------------------------- */
1227 /* --- Surface Attribs                                                   --- */
1228 /* ------------------------------------------------------------------------- */
1229
1230 static gboolean
1231 ensure_attributes (GstVaapiFilter * filter)
1232 {
1233   if (G_LIKELY (filter->attribs))
1234     return TRUE;
1235
1236   filter->attribs = gst_vaapi_config_surface_attributes_get (filter->display,
1237       filter->va_config);
1238   return (filter->attribs != NULL);
1239 }
1240
1241 static inline gboolean
1242 is_special_format (GstVideoFormat format)
1243 {
1244   return format == GST_VIDEO_FORMAT_UNKNOWN ||
1245       format == GST_VIDEO_FORMAT_ENCODED;
1246 }
1247
1248 static gboolean
1249 find_format (GstVaapiFilter * filter, GstVideoFormat format)
1250 {
1251   guint i;
1252   GArray *formats;
1253
1254   formats = filter->attribs->formats;
1255   if (is_special_format (format) || !formats)
1256     return FALSE;
1257
1258   for (i = 0; i < formats->len; i++) {
1259     if (g_array_index (formats, GstVideoFormat, i) == format)
1260       return TRUE;
1261   }
1262   return FALSE;
1263 }
1264
1265 /* ------------------------------------------------------------------------- */
1266 /* --- Interface                                                         --- */
1267 /* ------------------------------------------------------------------------- */
1268
1269 static void
1270 gst_vaapi_filter_init (GstVaapiFilter * filter)
1271 {
1272   filter->va_config = VA_INVALID_ID;
1273   filter->va_context = VA_INVALID_ID;
1274   filter->format = DEFAULT_FORMAT;
1275
1276   filter->forward_references =
1277       g_array_sized_new (FALSE, FALSE, sizeof (VASurfaceID), 4);
1278
1279   filter->backward_references =
1280       g_array_sized_new (FALSE, FALSE, sizeof (VASurfaceID), 4);
1281 }
1282
1283 static gboolean
1284 gst_vaapi_filter_initialize (GstVaapiFilter * filter)
1285 {
1286   VAStatus va_status;
1287
1288   if (!filter->display)
1289     return FALSE;
1290
1291   va_status = vaCreateConfig (filter->va_display, VAProfileNone,
1292       VAEntrypointVideoProc, NULL, 0, &filter->va_config);
1293   if (!vaapi_check_status (va_status, "vaCreateConfig() [VPP]"))
1294     return FALSE;
1295
1296   va_status = vaCreateContext (filter->va_display, filter->va_config, 0, 0, 0,
1297       NULL, 0, &filter->va_context);
1298   if (!vaapi_check_status (va_status, "vaCreateContext() [VPP]"))
1299     return FALSE;
1300
1301   gst_video_colorimetry_from_string (&filter->input_colorimetry, NULL);
1302   gst_video_colorimetry_from_string (&filter->output_colorimetry, NULL);
1303
1304   return TRUE;
1305 }
1306
1307 static void
1308 gst_vaapi_filter_finalize (GObject * object)
1309 {
1310   GstVaapiFilter *const filter = GST_VAAPI_FILTER (object);
1311   guint i;
1312
1313   if (!filter->display)
1314     goto bail;
1315
1316   GST_VAAPI_DISPLAY_LOCK (filter->display);
1317   if (filter->operations) {
1318     for (i = 0; i < filter->operations->len; i++) {
1319       GstVaapiFilterOpData *const op_data =
1320           g_ptr_array_index (filter->operations, i);
1321       vaapi_destroy_buffer (filter->va_display, &op_data->va_buffer);
1322     }
1323     g_ptr_array_unref (filter->operations);
1324     filter->operations = NULL;
1325   }
1326
1327   if (filter->va_context != VA_INVALID_ID) {
1328     vaDestroyContext (filter->va_display, filter->va_context);
1329     filter->va_context = VA_INVALID_ID;
1330   }
1331
1332   if (filter->va_config != VA_INVALID_ID) {
1333     vaDestroyConfig (filter->va_display, filter->va_config);
1334     filter->va_config = VA_INVALID_ID;
1335   }
1336   GST_VAAPI_DISPLAY_UNLOCK (filter->display);
1337   gst_vaapi_display_replace (&filter->display, NULL);
1338
1339 bail:
1340   if (filter->forward_references) {
1341     g_array_unref (filter->forward_references);
1342     filter->forward_references = NULL;
1343   }
1344
1345   if (filter->backward_references) {
1346     g_array_unref (filter->backward_references);
1347     filter->backward_references = NULL;
1348   }
1349
1350   if (filter->attribs) {
1351     gst_vaapi_config_surface_attributes_free (filter->attribs);
1352     filter->attribs = NULL;
1353   }
1354
1355   G_OBJECT_CLASS (gst_vaapi_filter_parent_class)->finalize (object);
1356 }
1357
1358 static void
1359 gst_vaapi_filter_set_property (GObject * object, guint property_id,
1360     const GValue * value, GParamSpec * pspec)
1361 {
1362   GstVaapiFilter *const filter = GST_VAAPI_FILTER (object);
1363
1364   switch (property_id) {
1365     case PROP_DISPLAY:{
1366       GstVaapiDisplay *display = g_value_get_object (value);;
1367
1368       if (display) {
1369         if (GST_VAAPI_DISPLAY_HAS_VPP (display)) {
1370           filter->display = gst_object_ref (display);
1371           filter->va_display = GST_VAAPI_DISPLAY_VADISPLAY (filter->display);
1372         } else {
1373           GST_WARNING_OBJECT (filter, "VA display doesn't support VPP");
1374         }
1375       }
1376       break;
1377     }
1378     default:
1379       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1380   }
1381 }
1382
1383 static void
1384 gst_vaapi_filter_get_property (GObject * object, guint property_id,
1385     GValue * value, GParamSpec * pspec)
1386 {
1387   GstVaapiFilter *const filter = GST_VAAPI_FILTER (object);
1388
1389   switch (property_id) {
1390     case PROP_DISPLAY:
1391       g_value_set_object (value, filter->display);
1392       break;
1393     default:
1394       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1395   }
1396 }
1397
1398 static void
1399 gst_vaapi_filter_class_init (GstVaapiFilterClass * klass)
1400 {
1401   GObjectClass *const object_class = G_OBJECT_CLASS (klass);
1402
1403   object_class->set_property = gst_vaapi_filter_set_property;
1404   object_class->get_property = gst_vaapi_filter_get_property;
1405   object_class->finalize = gst_vaapi_filter_finalize;
1406
1407   /**
1408    * GstVaapiFilter:display:
1409    *
1410    * #GstVaapiDisplay to be used.
1411    */
1412   g_object_class_install_property (object_class, PROP_DISPLAY,
1413       g_param_spec_object ("display", "Gst VA-API Display",
1414           "The VA-API display object to use", GST_TYPE_VAAPI_DISPLAY,
1415           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME));
1416 }
1417
1418 /**
1419  * gst_vaapi_filter_new:
1420  * @display: a #GstVaapiDisplay
1421  *
1422  * Creates a new #GstVaapiFilter set up to operate in "identity"
1423  * mode. This means that no other operation than scaling is performed.
1424  *
1425  * Return value: the newly created #GstVaapiFilter object
1426  */
1427 GstVaapiFilter *
1428 gst_vaapi_filter_new (GstVaapiDisplay * display)
1429 {
1430   GstVaapiFilter *filter;
1431
1432   filter = g_object_new (GST_TYPE_VAAPI_FILTER, "display", display, NULL);
1433   if (!gst_vaapi_filter_initialize (filter))
1434     goto error;
1435   return filter;
1436
1437   /* ERRORS */
1438 error:
1439   {
1440     gst_object_unref (filter);
1441     return NULL;
1442   }
1443 }
1444
1445 /**
1446  * gst_vaapi_filter_replace:
1447  * @old_filter_ptr: a pointer to a #GstVaapiFilter
1448  * @new_filter: a #GstVaapiFilter
1449  *
1450  * Atomically replaces the filter held in @old_filter_ptr with
1451  * @new_filter. This means that @old_filter_ptr shall reference a
1452  * valid filter. However, @new_filter can be NULL.
1453  */
1454 void
1455 gst_vaapi_filter_replace (GstVaapiFilter ** old_filter_ptr,
1456     GstVaapiFilter * new_filter)
1457 {
1458   g_return_if_fail (old_filter_ptr != NULL);
1459
1460   gst_object_replace ((GstObject **) old_filter_ptr, GST_OBJECT (new_filter));
1461 }
1462
1463 /**
1464  * gst_vaapi_filter_get_operations:
1465  * @filter: a #GstVaapiFilter, or %NULL
1466  *
1467  * Determines the set of supported operations for video processing.
1468  * The caller owns an extra reference to the resulting array of
1469  * #GstVaapiFilterOpInfo elements, so it shall be released with
1470  * g_ptr_array_unref() after usage.
1471  *
1472  * If @filter is %NULL, then this function returns the video
1473  * processing operations supported by this library.
1474  *
1475  * Return value: the set of supported operations, or %NULL if an error
1476  *   occurred.
1477  */
1478 GPtrArray *
1479 gst_vaapi_filter_get_operations (GstVaapiFilter * filter)
1480 {
1481   return get_operations (filter);
1482 }
1483
1484 /**
1485  * gst_vaapi_filter_has_operation:
1486  * @filter: a #GstVaapiFilter
1487  * @op: a #GstVaapiFilterOp
1488  *
1489  * Determines whether the underlying VA driver advertises support for
1490  * the supplied operation @op.
1491  *
1492  * Return value: %TRUE if the specified operation may be supported by
1493  *   the underlying hardware, %FALSE otherwise
1494  */
1495 gboolean
1496 gst_vaapi_filter_has_operation (GstVaapiFilter * filter, GstVaapiFilterOp op)
1497 {
1498   g_return_val_if_fail (filter != NULL, FALSE);
1499
1500   return find_operation (filter, op) != NULL;
1501 }
1502
1503 /**
1504  * gst_vaapi_filter_use_operation:
1505  * @filter: a #GstVaapiFilter
1506  * @op: a #GstVaapiFilterOp
1507  *
1508  * Determines whether the supplied operation @op was already enabled
1509  * through a prior call to gst_vaapi_filter_set_operation() or any
1510  * other operation-specific function.
1511  *
1512  * Note: should an operation be set to its default value, this means
1513  * that it is actually not enabled.
1514  *
1515  * Return value: %TRUE if the specified operation was already enabled,
1516  *   %FALSE otherwise
1517  */
1518 gboolean
1519 gst_vaapi_filter_use_operation (GstVaapiFilter * filter, GstVaapiFilterOp op)
1520 {
1521   GstVaapiFilterOpData *op_data;
1522
1523   g_return_val_if_fail (filter != NULL, FALSE);
1524
1525   op_data = find_operation (filter, op);
1526   if (!op_data)
1527     return FALSE;
1528   return op_data->is_enabled;
1529 }
1530
1531 /**
1532  * gst_vaapi_filter_set_operation:
1533  * @filter: a #GstVaapiFilter
1534  * @op: a #GstVaapiFilterOp
1535  * @value: the @op settings
1536  *
1537  * Enable the specified operation @op to be performed during video
1538  * processing, i.e. in gst_vaapi_filter_process(). The @value argument
1539  * specifies the operation settings. e.g. deinterlacing method for
1540  * deinterlacing, denoising level for noise reduction, etc.
1541  *
1542  * If @value is %NULL, then this function resets the operation
1543  * settings to their default values.
1544  *
1545  * Return value: %TRUE if the specified operation may be supported,
1546  *   %FALSE otherwise
1547  */
1548 gboolean
1549 gst_vaapi_filter_set_operation (GstVaapiFilter * filter, GstVaapiFilterOp op,
1550     const GValue * value)
1551 {
1552   GstVaapiFilterOpData *op_data;
1553
1554   g_return_val_if_fail (filter != NULL, FALSE);
1555
1556   op_data = find_operation (filter, op);
1557   if (!op_data)
1558     return FALSE;
1559
1560   if (value && !G_VALUE_HOLDS (value, G_PARAM_SPEC_VALUE_TYPE (op_data->pspec)))
1561     return FALSE;
1562
1563   switch (op) {
1564     case GST_VAAPI_FILTER_OP_FORMAT:
1565       return gst_vaapi_filter_set_format (filter, value ?
1566           g_value_get_enum (value) : DEFAULT_FORMAT);
1567     case GST_VAAPI_FILTER_OP_CROP:
1568       return gst_vaapi_filter_set_cropping_rectangle (filter, value ?
1569           g_value_get_boxed (value) : NULL);
1570     case GST_VAAPI_FILTER_OP_DENOISE:
1571     case GST_VAAPI_FILTER_OP_SHARPEN:
1572       return op_set_generic (filter, op_data,
1573           (value ? g_value_get_float (value) :
1574               OP_DATA_DEFAULT_VALUE (float, op_data)));
1575     case GST_VAAPI_FILTER_OP_HUE:
1576     case GST_VAAPI_FILTER_OP_SATURATION:
1577     case GST_VAAPI_FILTER_OP_BRIGHTNESS:
1578     case GST_VAAPI_FILTER_OP_CONTRAST:
1579       return op_set_color_balance (filter, op_data,
1580           (value ? g_value_get_float (value) :
1581               OP_DATA_DEFAULT_VALUE (float, op_data)));
1582     case GST_VAAPI_FILTER_OP_DEINTERLACING:
1583       return op_set_deinterlace (filter, op_data,
1584           (value ? g_value_get_enum (value) :
1585               OP_DATA_DEFAULT_VALUE (enum, op_data)), 0);
1586       break;
1587     case GST_VAAPI_FILTER_OP_SCALING:
1588       return gst_vaapi_filter_set_scaling (filter,
1589           (value ? g_value_get_enum (value) :
1590               OP_DATA_DEFAULT_VALUE (enum, op_data)));
1591 #ifndef GST_REMOVE_DEPRECATED
1592     case GST_VAAPI_FILTER_OP_SKINTONE:
1593       return op_set_skintone (filter, op_data,
1594           (value ? g_value_get_boolean (value) :
1595               OP_DATA_DEFAULT_VALUE (boolean, op_data)));
1596 #endif
1597     case GST_VAAPI_FILTER_OP_SKINTONE_LEVEL:
1598       return op_set_skintone_level (filter, op_data,
1599           (value ? g_value_get_uint (value) :
1600               OP_DATA_DEFAULT_VALUE (uint, op_data)));
1601     case GST_VAAPI_FILTER_OP_VIDEO_DIRECTION:
1602       return gst_vaapi_filter_set_video_direction (filter,
1603           (value ? g_value_get_enum (value) :
1604               OP_DATA_DEFAULT_VALUE (enum, op_data)));
1605     case GST_VAAPI_FILTER_OP_HDR_TONE_MAP:
1606       return op_set_hdr_tone_map (filter, op_data,
1607           (value ? g_value_get_boolean (value) :
1608               OP_DATA_DEFAULT_VALUE (boolean, op_data)));
1609     default:
1610       break;
1611   }
1612   return FALSE;
1613 }
1614
1615 #if VA_CHECK_VERSION(1,2,0)
1616 static void
1617 fill_color_standard (GstVideoColorimetry * colorimetry,
1618     VAProcColorStandardType * type, VAProcColorProperties * properties)
1619 {
1620   *type = from_GstVideoColorimetry (colorimetry);
1621
1622   properties->colour_primaries =
1623       gst_video_color_primaries_to_iso (colorimetry->primaries);
1624   properties->transfer_characteristics =
1625       gst_video_transfer_function_to_iso (colorimetry->transfer);
1626   properties->matrix_coefficients =
1627       gst_video_color_matrix_to_iso (colorimetry->matrix);
1628
1629   properties->color_range = from_GstVideoColorRange (colorimetry->range);
1630 }
1631 #endif
1632
1633 static void
1634 gst_vaapi_filter_fill_color_standards (GstVaapiFilter * filter,
1635     VAProcPipelineParameterBuffer * pipeline_param)
1636 {
1637 #if VA_CHECK_VERSION(1,2,0)
1638   fill_color_standard (&filter->input_colorimetry,
1639       &pipeline_param->surface_color_standard,
1640       &pipeline_param->input_color_properties);
1641
1642   fill_color_standard (&filter->output_colorimetry,
1643       &pipeline_param->output_color_standard,
1644       &pipeline_param->output_color_properties);
1645 #else
1646   pipeline_param->surface_color_standard = VAProcColorStandardNone;
1647   pipeline_param->output_color_standard = VAProcColorStandardNone;
1648 #endif
1649 }
1650
1651 /**
1652  * gst_vaapi_filter_process:
1653  * @filter: a #GstVaapiFilter
1654  * @src_surface: the source @GstVaapiSurface
1655  * @dst_surface: the destination @GstVaapiSurface
1656  * @flags: #GstVaapiSurfaceRenderFlags that apply to @src_surface
1657  *
1658  * Applies the operations currently defined in the @filter to
1659  * @src_surface and return the output in @dst_surface. The order of
1660  * operations is determined in a way that suits best the underlying
1661  * hardware. i.e. the only guarantee held is the generated outcome,
1662  * not any specific order of operations.
1663  *
1664  * Return value: a #GstVaapiFilterStatus
1665  */
1666 static GstVaapiFilterStatus
1667 gst_vaapi_filter_process_unlocked (GstVaapiFilter * filter,
1668     GstVaapiSurface * src_surface, GstVaapiSurface * dst_surface, guint flags)
1669 {
1670   VAProcPipelineParameterBuffer *pipeline_param = NULL;
1671   VABufferID pipeline_param_buf_id = VA_INVALID_ID;
1672   VABufferID filters[N_PROPERTIES];
1673   VAProcPipelineCaps pipeline_caps;
1674   guint i, num_filters = 0;
1675   VAStatus va_status;
1676   VARectangle src_rect, dst_rect;
1677   guint va_mirror = 0, va_rotation = 0;
1678
1679   if (!ensure_operations (filter))
1680     return GST_VAAPI_FILTER_STATUS_ERROR_ALLOCATION_FAILED;
1681
1682   /* Build surface region (source) */
1683   if (filter->use_crop_rect) {
1684     const GstVaapiRectangle *const crop_rect = &filter->crop_rect;
1685
1686     if ((crop_rect->x + crop_rect->width >
1687             GST_VAAPI_SURFACE_WIDTH (src_surface)) ||
1688         (crop_rect->y + crop_rect->height >
1689             GST_VAAPI_SURFACE_HEIGHT (src_surface)))
1690       goto error;
1691
1692     src_rect.x = crop_rect->x;
1693     src_rect.y = crop_rect->y;
1694     src_rect.width = crop_rect->width;
1695     src_rect.height = crop_rect->height;
1696   } else {
1697     src_rect.x = 0;
1698     src_rect.y = 0;
1699     src_rect.width = GST_VAAPI_SURFACE_WIDTH (src_surface);
1700     src_rect.height = GST_VAAPI_SURFACE_HEIGHT (src_surface);
1701   }
1702
1703   /* Build output region (target) */
1704   if (filter->use_target_rect) {
1705     const GstVaapiRectangle *const target_rect = &filter->target_rect;
1706
1707     if ((target_rect->x + target_rect->width >
1708             GST_VAAPI_SURFACE_WIDTH (dst_surface)) ||
1709         (target_rect->y + target_rect->height >
1710             GST_VAAPI_SURFACE_HEIGHT (dst_surface)))
1711       goto error;
1712
1713     dst_rect.x = target_rect->x;
1714     dst_rect.y = target_rect->y;
1715     dst_rect.width = target_rect->width;
1716     dst_rect.height = target_rect->height;
1717   } else {
1718     dst_rect.x = 0;
1719     dst_rect.y = 0;
1720     dst_rect.width = GST_VAAPI_SURFACE_WIDTH (dst_surface);
1721     dst_rect.height = GST_VAAPI_SURFACE_HEIGHT (dst_surface);
1722   }
1723
1724   for (i = 0, num_filters = 0; i < filter->operations->len; i++) {
1725     GstVaapiFilterOpData *const op_data =
1726         g_ptr_array_index (filter->operations, i);
1727     if (!op_data->is_enabled)
1728       continue;
1729     if (op_data->va_buffer == VA_INVALID_ID) {
1730       GST_ERROR ("invalid VA buffer for operation %s",
1731           g_param_spec_get_name (op_data->pspec));
1732       goto error;
1733     }
1734     filters[num_filters++] = op_data->va_buffer;
1735   }
1736
1737   /* Validate pipeline caps */
1738   va_status = vaQueryVideoProcPipelineCaps (filter->va_display,
1739       filter->va_context, filters, num_filters, &pipeline_caps);
1740   if (!vaapi_check_status (va_status, "vaQueryVideoProcPipelineCaps()"))
1741     goto error;
1742
1743   if (!vaapi_create_buffer (filter->va_display, filter->va_context,
1744           VAProcPipelineParameterBufferType, sizeof (*pipeline_param),
1745           NULL, &pipeline_param_buf_id, (gpointer *) & pipeline_param))
1746     goto error;
1747
1748   memset (pipeline_param, 0, sizeof (*pipeline_param));
1749   pipeline_param->surface = GST_VAAPI_SURFACE_ID (src_surface);
1750   pipeline_param->surface_region = &src_rect;
1751
1752   gst_vaapi_filter_fill_color_standards (filter, pipeline_param);
1753
1754   pipeline_param->output_region = &dst_rect;
1755   pipeline_param->output_background_color = 0xff000000;
1756   pipeline_param->filter_flags = from_GstVaapiSurfaceRenderFlags (flags) |
1757       from_GstVaapiScaleMethod (filter->scale_method);
1758   pipeline_param->filters = filters;
1759   pipeline_param->num_filters = num_filters;
1760
1761   from_GstVideoOrientationMethod (filter->video_direction, &va_mirror,
1762       &va_rotation);
1763
1764 #if VA_CHECK_VERSION(1,1,0)
1765   pipeline_param->mirror_state = va_mirror;
1766   pipeline_param->rotation_state = va_rotation;
1767 #endif
1768
1769   // Reference frames for advanced deinterlacing
1770   if (filter->forward_references->len > 0) {
1771     pipeline_param->forward_references = (VASurfaceID *)
1772         filter->forward_references->data;
1773     pipeline_param->num_forward_references =
1774         MIN (filter->forward_references->len,
1775         pipeline_caps.num_forward_references);
1776   } else {
1777     pipeline_param->forward_references = NULL;
1778     pipeline_param->num_forward_references = 0;
1779   }
1780
1781   if (filter->backward_references->len > 0) {
1782     pipeline_param->backward_references = (VASurfaceID *)
1783         filter->backward_references->data;
1784     pipeline_param->num_backward_references =
1785         MIN (filter->backward_references->len,
1786         pipeline_caps.num_backward_references);
1787   } else {
1788     pipeline_param->backward_references = NULL;
1789     pipeline_param->num_backward_references = 0;
1790   }
1791
1792   vaapi_unmap_buffer (filter->va_display, pipeline_param_buf_id, NULL);
1793
1794   va_status = vaBeginPicture (filter->va_display, filter->va_context,
1795       GST_VAAPI_SURFACE_ID (dst_surface));
1796   if (!vaapi_check_status (va_status, "vaBeginPicture()"))
1797     goto error;
1798
1799   va_status = vaRenderPicture (filter->va_display, filter->va_context,
1800       &pipeline_param_buf_id, 1);
1801   if (!vaapi_check_status (va_status, "vaRenderPicture()"))
1802     goto error;
1803
1804   va_status = vaEndPicture (filter->va_display, filter->va_context);
1805   if (!vaapi_check_status (va_status, "vaEndPicture()"))
1806     goto error;
1807
1808   deint_refs_clear_all (filter);
1809   vaapi_destroy_buffer (filter->va_display, &pipeline_param_buf_id);
1810   return GST_VAAPI_FILTER_STATUS_SUCCESS;
1811
1812   /* ERRORS */
1813 error:
1814   {
1815     deint_refs_clear_all (filter);
1816     vaapi_destroy_buffer (filter->va_display, &pipeline_param_buf_id);
1817     return GST_VAAPI_FILTER_STATUS_ERROR_OPERATION_FAILED;
1818   }
1819 }
1820
1821 GstVaapiFilterStatus
1822 gst_vaapi_filter_process (GstVaapiFilter * filter,
1823     GstVaapiSurface * src_surface, GstVaapiSurface * dst_surface, guint flags)
1824 {
1825   GstVaapiFilterStatus status;
1826
1827   g_return_val_if_fail (filter != NULL,
1828       GST_VAAPI_FILTER_STATUS_ERROR_INVALID_PARAMETER);
1829   g_return_val_if_fail (src_surface != NULL,
1830       GST_VAAPI_FILTER_STATUS_ERROR_INVALID_PARAMETER);
1831   g_return_val_if_fail (dst_surface != NULL,
1832       GST_VAAPI_FILTER_STATUS_ERROR_INVALID_PARAMETER);
1833
1834   GST_VAAPI_DISPLAY_LOCK (filter->display);
1835   status = gst_vaapi_filter_process_unlocked (filter,
1836       src_surface, dst_surface, flags);
1837   GST_VAAPI_DISPLAY_UNLOCK (filter->display);
1838   return status;
1839 }
1840
1841 /**
1842  * gst_vaapi_filter_get_formats:
1843  * @filter: a #GstVaapiFilter
1844  *
1845  * Determines the set of supported source or target formats for video
1846  * processing.  The caller owns an extra reference to the resulting
1847  * array of #GstVideoFormat elements, so it shall be released with
1848  * g_array_unref() after usage.
1849  *
1850  * Return value: the set of supported target formats for video processing.
1851  */
1852 GArray *
1853 gst_vaapi_filter_get_formats (GstVaapiFilter * filter)
1854 {
1855   g_return_val_if_fail (filter != NULL, NULL);
1856
1857   if (!ensure_attributes (filter))
1858     return NULL;
1859   if (filter->attribs->formats)
1860     return g_array_ref (filter->attribs->formats);
1861   return NULL;
1862 }
1863
1864 /**
1865  * gst_vaapi_filter_set_format:
1866  * @filter: a #GstVaapiFilter
1867  * @format: the target surface format
1868  *
1869  * Sets the desired pixel format of the resulting video processing
1870  * operations.
1871  *
1872  * If @format is #GST_VIDEO_FORMAT_UNKNOWN, the filter will assume iso
1873  * format conversion, i.e. no color conversion at all and the target
1874  * surface format shall match the source surface format.
1875  *
1876  * If @format is #GST_VIDEO_FORMAT_ENCODED, the filter will use the pixel
1877  * format of the target surface passed to gst_vaapi_filter_process().
1878  *
1879  * Return value: %TRUE if the color conversion to the specified @format
1880  *   may be supported, %FALSE otherwise.
1881  */
1882 gboolean
1883 gst_vaapi_filter_set_format (GstVaapiFilter * filter, GstVideoFormat format)
1884 {
1885   g_return_val_if_fail (filter != NULL, FALSE);
1886
1887   if (!ensure_attributes (filter))
1888     return FALSE;
1889
1890   if (!is_special_format (format) && !find_format (filter, format))
1891     return FALSE;
1892
1893   filter->format = format;
1894   return TRUE;
1895 }
1896
1897 /**
1898  * gst_vaapi_filter_append_caps:
1899  * @filter: a #GstVaapiFilter
1900  * @structure: a #GstStructure from #GstCaps
1901  *
1902  * Extracts the config's surface attributes, from @filter's context,
1903  * and transforms it into a caps formats and appended them into
1904  * @structure.
1905  *
1906  * Returns: %TRUE if the capabilities could be extracted and appended
1907  * into @structure; otherwise %FALSE
1908  **/
1909 gboolean
1910 gst_vaapi_filter_append_caps (GstVaapiFilter * filter, GstStructure * structure)
1911 {
1912   GstVaapiConfigSurfaceAttributes *attribs;
1913
1914   g_return_val_if_fail (filter != NULL, FALSE);
1915   g_return_val_if_fail (structure != NULL, FALSE);
1916
1917   if (!ensure_attributes (filter))
1918     return FALSE;
1919
1920   attribs = filter->attribs;
1921
1922   if (attribs->min_width >= attribs->max_width ||
1923       attribs->min_height >= attribs->max_height)
1924     return FALSE;
1925
1926   gst_structure_set (structure, "width", GST_TYPE_INT_RANGE, attribs->min_width,
1927       attribs->max_width, "height", GST_TYPE_INT_RANGE, attribs->min_height,
1928       attribs->max_height, NULL);
1929
1930   return TRUE;
1931
1932 }
1933
1934 /**
1935  * gst_vaapi_filter_get_memory_types:
1936  * @filter: a #GstVaapiFilter
1937  *
1938  * Gets the surface's memory types available in @filter's context.
1939  *
1940  * Returns: surface's memory types available in @filter context.
1941  **/
1942 guint
1943 gst_vaapi_filter_get_memory_types (GstVaapiFilter * filter)
1944 {
1945   g_return_val_if_fail (filter != NULL, FALSE);
1946
1947   if (!ensure_attributes (filter))
1948     return 0;
1949   return filter->attribs->mem_types;
1950 }
1951
1952 /**
1953  * gst_vaapi_filter_set_cropping_rectangle:
1954  * @filter: a #GstVaapiFilter
1955  * @rect: the cropping region
1956  *
1957  * Sets the source surface cropping rectangle to use during the video
1958  * processing. If @rect is %NULL, the whole source surface will be used.
1959  *
1960  * Return value: %TRUE if the operation is supported, %FALSE otherwise.
1961  */
1962 gboolean
1963 gst_vaapi_filter_set_cropping_rectangle (GstVaapiFilter * filter,
1964     const GstVaapiRectangle * rect)
1965 {
1966   g_return_val_if_fail (filter != NULL, FALSE);
1967
1968   filter->use_crop_rect = rect != NULL;
1969   if (filter->use_crop_rect)
1970     filter->crop_rect = *rect;
1971   return TRUE;
1972 }
1973
1974 /**
1975  * gst_vaapi_filter_set_target_rectangle:
1976  * @filter: a #GstVaapiFilter
1977  * @rect: the target render region
1978  *
1979  * Sets the region within the target surface where the source surface
1980  * would be rendered. i.e. where the hardware accelerator would emit
1981  * the outcome of video processing. If @rect is %NULL, the whole
1982  * source surface will be used.
1983  *
1984  * Return value: %TRUE if the operation is supported, %FALSE otherwise.
1985  */
1986 gboolean
1987 gst_vaapi_filter_set_target_rectangle (GstVaapiFilter * filter,
1988     const GstVaapiRectangle * rect)
1989 {
1990   g_return_val_if_fail (filter != NULL, FALSE);
1991
1992   filter->use_target_rect = rect != NULL;
1993   if (filter->use_target_rect)
1994     filter->target_rect = *rect;
1995   return TRUE;
1996 }
1997
1998 /**
1999  * gst_vaapi_filter_set_denoising_level:
2000  * @filter: a #GstVaapiFilter
2001  * @level: the level of noise reduction to apply
2002  *
2003  * Sets the noise reduction level to apply. If @level is 0.0f, this
2004  * corresponds to disabling the noise reduction algorithm.
2005  *
2006  * Return value: %TRUE if the operation is supported, %FALSE otherwise.
2007  */
2008 gboolean
2009 gst_vaapi_filter_set_denoising_level (GstVaapiFilter * filter, gfloat level)
2010 {
2011   g_return_val_if_fail (filter != NULL, FALSE);
2012
2013   return op_set_generic (filter,
2014       find_operation (filter, GST_VAAPI_FILTER_OP_DENOISE), level);
2015 }
2016
2017 /**
2018  * gst_vaapi_filter_set_sharpening_level:
2019  * @filter: a #GstVaapiFilter
2020  * @level: the sharpening factor
2021  *
2022  * Enables noise reduction with the specified factor.
2023  *
2024  * Return value: %TRUE if the operation is supported, %FALSE otherwise.
2025  */
2026 gboolean
2027 gst_vaapi_filter_set_sharpening_level (GstVaapiFilter * filter, gfloat level)
2028 {
2029   g_return_val_if_fail (filter != NULL, FALSE);
2030
2031   return op_set_generic (filter,
2032       find_operation (filter, GST_VAAPI_FILTER_OP_SHARPEN), level);
2033 }
2034
2035 /**
2036  * gst_vaapi_filter_set_hue:
2037  * @filter: a #GstVaapiFilter
2038  * @value: the color hue value
2039  *
2040  * Enables color hue adjustment to the specified value.
2041  *
2042  * Return value: %TRUE if the operation is supported, %FALSE otherwise.
2043  */
2044 gboolean
2045 gst_vaapi_filter_set_hue (GstVaapiFilter * filter, gfloat value)
2046 {
2047   g_return_val_if_fail (filter != NULL, FALSE);
2048
2049   return op_set_color_balance (filter,
2050       find_operation (filter, GST_VAAPI_FILTER_OP_HUE), value);
2051 }
2052
2053 /**
2054  * gst_vaapi_filter_set_saturation:
2055  * @filter: a #GstVaapiFilter
2056  * @value: the color saturation value
2057  *
2058  * Enables color saturation adjustment to the specified value.
2059  *
2060  * Return value: %TRUE if the operation is supported, %FALSE otherwise.
2061  */
2062 gboolean
2063 gst_vaapi_filter_set_saturation (GstVaapiFilter * filter, gfloat value)
2064 {
2065   g_return_val_if_fail (filter != NULL, FALSE);
2066
2067   return op_set_color_balance (filter,
2068       find_operation (filter, GST_VAAPI_FILTER_OP_SATURATION), value);
2069 }
2070
2071 /**
2072  * gst_vaapi_filter_set_brightness:
2073  * @filter: a #GstVaapiFilter
2074  * @value: the color brightness value
2075  *
2076  * Enables color brightness adjustment to the specified value.
2077  *
2078  * Return value: %TRUE if the operation is supported, %FALSE otherwise.
2079  */
2080 gboolean
2081 gst_vaapi_filter_set_brightness (GstVaapiFilter * filter, gfloat value)
2082 {
2083   g_return_val_if_fail (filter != NULL, FALSE);
2084
2085   return op_set_color_balance (filter,
2086       find_operation (filter, GST_VAAPI_FILTER_OP_BRIGHTNESS), value);
2087 }
2088
2089 /**
2090  * gst_vaapi_filter_set_contrast:
2091  * @filter: a #GstVaapiFilter
2092  * @value: the color contrast value
2093  *
2094  * Enables color contrast adjustment to the specified value.
2095  *
2096  * Return value: %TRUE if the operation is supported, %FALSE otherwise.
2097  */
2098 gboolean
2099 gst_vaapi_filter_set_contrast (GstVaapiFilter * filter, gfloat value)
2100 {
2101   g_return_val_if_fail (filter != NULL, FALSE);
2102
2103   return op_set_color_balance (filter,
2104       find_operation (filter, GST_VAAPI_FILTER_OP_CONTRAST), value);
2105 }
2106
2107 /**
2108  * gst_vaapi_filter_set_deinterlacing:
2109  * @filter: a #GstVaapiFilter
2110  * @method: the deinterlacing algorithm (see #GstVaapiDeinterlaceMethod)
2111  * @flags: the additional flags
2112  *
2113  * Applies deinterlacing to the video processing pipeline. If @method
2114  * is not @GST_VAAPI_DEINTERLACE_METHOD_NONE, then @flags could
2115  * represent the initial picture structure of the source frame.
2116  *
2117  * Return value: %TRUE if the operation is supported, %FALSE otherwise.
2118  */
2119 gboolean
2120 gst_vaapi_filter_set_deinterlacing (GstVaapiFilter * filter,
2121     GstVaapiDeinterlaceMethod method, guint flags)
2122 {
2123   g_return_val_if_fail (filter != NULL, FALSE);
2124
2125   return op_set_deinterlace (filter,
2126       find_operation (filter, GST_VAAPI_FILTER_OP_DEINTERLACING), method,
2127       flags);
2128 }
2129
2130 /**
2131  * gst_vaapi_filter_set_deinterlacing_references:
2132  * @filter: a #GstVaapiFilter
2133  * @forward_references: the set of #GstVaapiSurface objects used as
2134  *   forward references
2135  * @num_forward_references: the number of elements in the
2136  *   @forward_references array
2137  * @backward_references: the set of #GstVaapiSurface objects used as
2138  *   backward references
2139  * @num_backward_references: the number of elements in the
2140  *   @backward_references array
2141  *
2142  * Specifies the list of surfaces used for forward or backward reference in
2143  * advanced deinterlacing mode. The caller is responsible for maintaining
2144  * the associated surfaces live until gst_vaapi_filter_process() completes.
2145  * e.g. by holding an extra reference to the associated #GstVaapiSurfaceProxy.
2146  *
2147  * Temporal ordering is maintained as follows: the shorter index in
2148  * either array is, the closest the matching surface is relatively to
2149  * the current source surface to process. e.g. surface in
2150  * @forward_references array index 0 represents the immediately
2151  * preceding surface in display order, surface at index 1 is the one
2152  * preceding surface at index 0, etc.
2153  *
2154  * The video processing filter will only use the recommended number of
2155  * surfaces for backward and forward references.
2156  *
2157  * Note: the supplied lists of reference surfaces are not sticky. This
2158  * means that they are only valid for the next gst_vaapi_filter_process()
2159  * call, and thus needs to be submitted again for subsequent calls.
2160  *
2161  * Return value: %TRUE if the operation is supported, %FALSE otherwise.
2162  */
2163 gboolean
2164 gst_vaapi_filter_set_deinterlacing_references (GstVaapiFilter * filter,
2165     GstVaapiSurface ** forward_references, guint num_forward_references,
2166     GstVaapiSurface ** backward_references, guint num_backward_references)
2167 {
2168   g_return_val_if_fail (filter != NULL, FALSE);
2169
2170   deint_refs_clear_all (filter);
2171
2172   if (!deint_refs_set (filter->forward_references, forward_references,
2173           num_forward_references))
2174     return FALSE;
2175
2176   if (!deint_refs_set (filter->backward_references, backward_references,
2177           num_backward_references))
2178     return FALSE;
2179   return TRUE;
2180 }
2181
2182 /**
2183  * gst_vaapi_filter_set_scaling:
2184  * @filter: a #GstVaapiFilter
2185  * @method: the scaling algorithm (see #GstVaapiScaleMethod)
2186  *
2187  * Applies scaling algorithm to the video processing pipeline.
2188  *
2189  * Return value: %TRUE if the operation is supported, %FALSE otherwise.
2190  */
2191 gboolean
2192 gst_vaapi_filter_set_scaling (GstVaapiFilter * filter,
2193     GstVaapiScaleMethod method)
2194 {
2195   g_return_val_if_fail (filter != NULL, FALSE);
2196
2197   filter->scale_method = method;
2198   return TRUE;
2199 }
2200
2201 #ifndef GST_REMOVE_DEPRECATED
2202 /**
2203  * gst_vaapi_filter_set_skintone:
2204  * @filter: a #GstVaapiFilter
2205  * @enhance: %TRUE if enable the skin tone enhancement algorithm
2206  *
2207  * Applies the skin tone enhancement algorithm.
2208  *
2209  * Return value: %TRUE if the operation is supported, %FALSE
2210  * otherwise.
2211   **/
2212 gboolean
2213 gst_vaapi_filter_set_skintone (GstVaapiFilter * filter, gboolean enhance)
2214 {
2215   g_return_val_if_fail (filter != NULL, FALSE);
2216
2217   return op_set_skintone (filter,
2218       find_operation (filter, GST_VAAPI_FILTER_OP_SKINTONE), enhance);
2219 }
2220 #endif
2221
2222 /**
2223  * gst_vaapi_filter_set_skintone_level:
2224  * @filter: a #GstVaapiFilter
2225  * @value: the value if enable the skin tone enhancement algorithm
2226  *
2227  * Applies the skin tone enhancement algorithm with specifled value.
2228  *
2229  * Return value: %TRUE if the operation is supported, %FALSE
2230  * otherwise.
2231   **/
2232 gboolean
2233 gst_vaapi_filter_set_skintone_level (GstVaapiFilter * filter, guint value)
2234 {
2235   g_return_val_if_fail (filter != NULL, FALSE);
2236
2237   return op_set_skintone_level (filter,
2238       find_operation (filter, GST_VAAPI_FILTER_OP_SKINTONE_LEVEL), value);
2239 }
2240
2241 /**
2242  * gst_vaapi_filter_set_video_direction:
2243  * @filter: a #GstVaapiFilter
2244  * @method: the video direction (see #GstVideoOrientationMethod)
2245  *
2246  * Applies mirror/rotation to the video processing pipeline.
2247  *
2248  * Return value: %TRUE if the operation is supported, %FALSE otherwise.
2249  */
2250 gboolean
2251 gst_vaapi_filter_set_video_direction (GstVaapiFilter * filter,
2252     GstVideoOrientationMethod method)
2253 {
2254   g_return_val_if_fail (filter != NULL, FALSE);
2255
2256 #if VA_CHECK_VERSION(1,1,0)
2257   {
2258     guint32 va_mirror = VA_MIRROR_NONE;
2259     guint32 va_rotation = VA_ROTATION_NONE;
2260
2261     from_GstVideoOrientationMethod (method, &va_mirror, &va_rotation);
2262
2263     if (va_mirror != VA_MIRROR_NONE && !(filter->mirror_flags & va_mirror))
2264       return FALSE;
2265
2266     if (va_rotation != VA_ROTATION_NONE
2267         && !(filter->rotation_flags & (1 << va_rotation)))
2268       return FALSE;
2269   }
2270 #else
2271   return FALSE;
2272 #endif
2273
2274   filter->video_direction = method;
2275   return TRUE;
2276 }
2277
2278 /**
2279  * gst_vaapi_filter_get_video_direction:
2280  * @filter: a #GstVaapiFilter
2281  *
2282  * Return value: the currently applied video direction (see #GstVideoOrientationMethod)
2283  */
2284 GstVideoOrientationMethod
2285 gst_vaapi_filter_get_video_direction (GstVaapiFilter * filter)
2286 {
2287   g_return_val_if_fail (filter != NULL, GST_VIDEO_ORIENTATION_IDENTITY);
2288   return filter->video_direction;
2289 }
2290
2291 gfloat
2292 gst_vaapi_filter_get_denoising_level_default (GstVaapiFilter * filter)
2293 {
2294   OP_RET_DEFAULT_VALUE (float, filter, GST_VAAPI_FILTER_OP_DENOISE);
2295 }
2296
2297 gfloat
2298 gst_vaapi_filter_get_sharpening_level_default (GstVaapiFilter * filter)
2299 {
2300   OP_RET_DEFAULT_VALUE (float, filter, GST_VAAPI_FILTER_OP_SHARPEN);
2301 }
2302
2303 gfloat
2304 gst_vaapi_filter_get_hue_default (GstVaapiFilter * filter)
2305 {
2306   OP_RET_DEFAULT_VALUE (float, filter, GST_VAAPI_FILTER_OP_HUE);
2307 }
2308
2309 gfloat
2310 gst_vaapi_filter_get_saturation_default (GstVaapiFilter * filter)
2311 {
2312   OP_RET_DEFAULT_VALUE (float, filter, GST_VAAPI_FILTER_OP_SATURATION);
2313 }
2314
2315 gfloat
2316 gst_vaapi_filter_get_brightness_default (GstVaapiFilter * filter)
2317 {
2318   OP_RET_DEFAULT_VALUE (float, filter, GST_VAAPI_FILTER_OP_BRIGHTNESS);
2319 }
2320
2321 gfloat
2322 gst_vaapi_filter_get_contrast_default (GstVaapiFilter * filter)
2323 {
2324   OP_RET_DEFAULT_VALUE (float, filter, GST_VAAPI_FILTER_OP_CONTRAST);
2325 }
2326
2327 GstVaapiScaleMethod
2328 gst_vaapi_filter_get_scaling_default (GstVaapiFilter * filter)
2329 {
2330   OP_RET_DEFAULT_VALUE (enum, filter, GST_VAAPI_FILTER_OP_SCALING);
2331 }
2332
2333 #ifndef GST_REMOVE_DEPRECATED
2334 gboolean
2335 gst_vaapi_filter_get_skintone_default (GstVaapiFilter * filter)
2336 {
2337   OP_RET_DEFAULT_VALUE (boolean, filter, GST_VAAPI_FILTER_OP_SKINTONE);
2338 }
2339 #endif
2340
2341 guint
2342 gst_vaapi_filter_get_skintone_level_default (GstVaapiFilter * filter)
2343 {
2344   OP_RET_DEFAULT_VALUE (uint, filter, GST_VAAPI_FILTER_OP_SKINTONE_LEVEL);
2345 }
2346
2347 GstVideoOrientationMethod
2348 gst_vaapi_filter_get_video_direction_default (GstVaapiFilter * filter)
2349 {
2350   OP_RET_DEFAULT_VALUE (enum, filter, GST_VAAPI_FILTER_OP_VIDEO_DIRECTION);
2351 }
2352
2353 static gboolean
2354 gst_vaapi_filter_set_colorimetry_unlocked (GstVaapiFilter * filter,
2355     GstVideoColorimetry * input, GstVideoColorimetry * output)
2356 {
2357   gchar *in_color, *out_color;
2358
2359   if (input)
2360     filter->input_colorimetry = *input;
2361   else
2362     gst_video_colorimetry_from_string (&filter->input_colorimetry, NULL);
2363
2364   if (output)
2365     filter->output_colorimetry = *output;
2366   else
2367     gst_video_colorimetry_from_string (&filter->output_colorimetry, NULL);
2368
2369   in_color = gst_video_colorimetry_to_string (&filter->input_colorimetry);
2370   GST_DEBUG_OBJECT (filter, " input colorimetry '%s'", in_color);
2371
2372   out_color = gst_video_colorimetry_to_string (&filter->output_colorimetry);
2373   GST_DEBUG_OBJECT (filter, "output colorimetry '%s'", out_color);
2374
2375   if (!gst_vaapi_display_has_driver_quirks (filter->display,
2376           GST_VAAPI_DRIVER_QUIRK_NO_CHECK_VPP_COLOR_STD)) {
2377     VAProcPipelineCaps pipeline_caps = { 0, };
2378     VAProcColorStandardType type;
2379     guint32 i;
2380
2381     VAStatus va_status = vaQueryVideoProcPipelineCaps (filter->va_display,
2382         filter->va_context, NULL, 0, &pipeline_caps);
2383
2384     if (!vaapi_check_status (va_status, "vaQueryVideoProcPipelineCaps()"))
2385       return FALSE;
2386
2387     type = from_GstVideoColorimetry (&filter->input_colorimetry);
2388     for (i = 0; i < pipeline_caps.num_input_color_standards; i++)
2389       if (type == pipeline_caps.input_color_standards[i])
2390         break;
2391     if ((i == pipeline_caps.num_input_color_standards)
2392         && (type != VAProcColorStandardNone))
2393       GST_WARNING_OBJECT (filter,
2394           "driver does not support '%s' input colorimetry."
2395           " vpp may fail or produce unexpected results.", in_color);
2396
2397     type = from_GstVideoColorimetry (&filter->output_colorimetry);
2398     for (i = 0; i < pipeline_caps.num_output_color_standards; i++)
2399       if (type == pipeline_caps.output_color_standards[i])
2400         break;
2401     if ((i == pipeline_caps.num_output_color_standards)
2402         && (type != VAProcColorStandardNone))
2403       GST_WARNING_OBJECT (filter,
2404           "driver does not support '%s' output colorimetry."
2405           " vpp may fail or produce unexpected results.", out_color);
2406   } else {
2407     GST_WARNING_OBJECT (filter,
2408         "driver does not report the supported input/output colorimetry."
2409         " vpp may fail or produce unexpected results.");
2410   }
2411
2412   g_free (in_color);
2413   g_free (out_color);
2414
2415   return TRUE;
2416 }
2417
2418 gboolean
2419 gst_vaapi_filter_set_colorimetry (GstVaapiFilter * filter,
2420     GstVideoColorimetry * input, GstVideoColorimetry * output)
2421 {
2422   gboolean result;
2423
2424   g_return_val_if_fail (filter != NULL, FALSE);
2425
2426   GST_VAAPI_DISPLAY_LOCK (filter->display);
2427   result = gst_vaapi_filter_set_colorimetry_unlocked (filter, input, output);
2428   GST_VAAPI_DISPLAY_UNLOCK (filter->display);
2429
2430   return result;
2431 }
2432
2433 /**
2434  * gst_vaapi_filter_set_hdr_tone_map:
2435  * @filter: a #GstVaapiFilter
2436  * @value: %TRUE to enable hdr tone map algorithm
2437  *
2438  * Applies HDR tone mapping algorithm.
2439  *
2440  * Return value: %TRUE if the operation is supported, %FALSE otherwise.
2441  */
2442 gboolean
2443 gst_vaapi_filter_set_hdr_tone_map (GstVaapiFilter * filter, gboolean value)
2444 {
2445   g_return_val_if_fail (filter != NULL, FALSE);
2446
2447   return op_set_hdr_tone_map (filter,
2448       find_operation (filter, GST_VAAPI_FILTER_OP_HDR_TONE_MAP), value);
2449 }
2450
2451 static gboolean
2452 gst_vaapi_filter_set_hdr_tone_map_meta_unlocked (GstVaapiFilter * filter,
2453     GstVideoMasteringDisplayInfo * minfo, GstVideoContentLightLevel * linfo)
2454 {
2455 #if VA_CHECK_VERSION(1,4,0)
2456   GstVaapiFilterOpData *op_data;
2457   VAProcFilterParameterBufferHDRToneMapping *buf;
2458   VAHdrMetaDataHDR10 *meta = &filter->hdr_meta;
2459
2460   op_data = find_operation (filter, GST_VAAPI_FILTER_OP_HDR_TONE_MAP);
2461
2462   if (!op_data)
2463     return FALSE;
2464
2465   meta->display_primaries_x[0] = minfo->display_primaries[1].x;
2466   meta->display_primaries_x[1] = minfo->display_primaries[2].x;
2467   meta->display_primaries_x[2] = minfo->display_primaries[0].x;
2468
2469   meta->display_primaries_y[0] = minfo->display_primaries[1].y;
2470   meta->display_primaries_y[1] = minfo->display_primaries[2].y;
2471   meta->display_primaries_y[2] = minfo->display_primaries[0].y;
2472
2473   meta->white_point_x = minfo->white_point.x;
2474   meta->white_point_y = minfo->white_point.y;
2475
2476   meta->max_display_mastering_luminance =
2477       minfo->max_display_mastering_luminance;
2478   meta->min_display_mastering_luminance =
2479       minfo->min_display_mastering_luminance;
2480
2481   meta->max_content_light_level = linfo->max_content_light_level;
2482   meta->max_pic_average_light_level = linfo->max_frame_average_light_level;
2483
2484   buf = vaapi_map_buffer (filter->va_display, op_data->va_buffer);
2485   if (!buf)
2486     return FALSE;
2487
2488   buf->type = op_data->va_type;
2489   buf->data.metadata_type = op_data->va_subtype;
2490   buf->data.metadata = meta;
2491   buf->data.metadata_size = sizeof (meta);
2492
2493   vaapi_unmap_buffer (filter->va_display, op_data->va_buffer, NULL);
2494
2495   return TRUE;
2496 #else
2497   return FALSE;
2498 #endif
2499 }
2500
2501 /**
2502  * gst_vaapi_filter_set_hdr_tone_map_meta:
2503  * @filter: a #GstVaapiFilter
2504  * @minfo: a #GstVideoMasteringDisplayInfo
2505  * @linfo: a #GstVideoContentLightLevel
2506  *
2507  * Sets the input HDR meta data used for tone mapping.
2508  *
2509  * Return value: %TRUE if the operation is supported, %FALSE otherwise.
2510  */
2511 gboolean
2512 gst_vaapi_filter_set_hdr_tone_map_meta (GstVaapiFilter * filter,
2513     GstVideoMasteringDisplayInfo * minfo, GstVideoContentLightLevel * linfo)
2514 {
2515   gboolean status = FALSE;
2516
2517   g_return_val_if_fail (filter != NULL, FALSE);
2518   g_return_val_if_fail (minfo != NULL, FALSE);
2519   g_return_val_if_fail (linfo != NULL, FALSE);
2520
2521   GST_VAAPI_DISPLAY_LOCK (filter->display);
2522   status =
2523       gst_vaapi_filter_set_hdr_tone_map_meta_unlocked (filter, minfo, linfo);
2524   GST_VAAPI_DISPLAY_UNLOCK (filter->display);
2525
2526   return status;
2527 }