filter: add support for color balance adjustment.
[platform/upstream/gstreamer-vaapi.git] / gst-libs / gst / vaapi / gstvaapifilter.c
1 /*
2  *  gstvaapifilter.c - Video processing abstraction
3  *
4  *  Copyright (C) 2013 Intel Corporation
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public License
8  *  as published by the Free Software Foundation; either version 2.1
9  *  of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free
18  *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  *  Boston, MA 02110-1301 USA
20  */
21
22 #include "sysdeps.h"
23 #include "gstvaapifilter.h"
24 #include "gstvaapiutils.h"
25 #include "gstvaapivalue.h"
26 #include "gstvaapiminiobject.h"
27 #include "gstvaapidisplay_priv.h"
28 #include "gstvaapisurface_priv.h"
29
30 #if USE_VA_VPP
31 # include <va/va_vpp.h>
32 #endif
33
34 #define DEBUG 1
35 #include "gstvaapidebug.h"
36
37 #define GST_VAAPI_FILTER(obj) \
38     ((GstVaapiFilter *)(obj))
39
40 typedef struct _GstVaapiFilterOpData GstVaapiFilterOpData;
41 struct _GstVaapiFilterOpData {
42     GstVaapiFilterOp    op;
43     GParamSpec         *pspec;
44     volatile gint       ref_count;
45     guint               va_type;
46     guint               va_subtype;
47     gpointer            va_caps;
48     guint               va_num_caps;
49     guint               va_cap_size;
50     VABufferID          va_buffer;
51     guint               va_buffer_size;
52     guint               is_enabled      : 1;
53 };
54
55 struct _GstVaapiFilter {
56     /*< private >*/
57     GstVaapiMiniObject  parent_instance;
58
59     GstVaapiDisplay    *display;
60     VADisplay           va_display;
61     VAConfigID          va_config;
62     VAContextID         va_context;
63     GPtrArray          *operations;
64     GstVideoFormat      format;
65     GArray             *formats;
66     GstVaapiRectangle   crop_rect;
67     guint               use_crop_rect   : 1;
68 };
69
70 /* ------------------------------------------------------------------------- */
71 /* --- VPP Helpers                                                       --- */
72 /* ------------------------------------------------------------------------- */
73
74 #if USE_VA_VPP
75 static VAProcFilterType *
76 vpp_get_filters_unlocked(GstVaapiFilter *filter, guint *num_filters_ptr)
77 {
78     VAProcFilterType *filters = NULL;
79     guint num_filters = 0;
80     VAStatus va_status;
81
82     num_filters = VAProcFilterCount;
83     filters = g_malloc_n(num_filters, sizeof(*filters));
84     if (!filters)
85         goto error;
86
87     va_status = vaQueryVideoProcFilters(filter->va_display, filter->va_context,
88         filters, &num_filters);
89
90     // Try to reallocate to the expected number of filters
91     if (va_status == VA_STATUS_ERROR_MAX_NUM_EXCEEDED) {
92         VAProcFilterType * const new_filters =
93             g_try_realloc_n(filters, num_filters, sizeof(*new_filters));
94         if (!new_filters)
95             goto error;
96         filters = new_filters;
97
98         va_status = vaQueryVideoProcFilters(filter->va_display,
99             filter->va_context, filters, &num_filters);
100     }
101     if (!vaapi_check_status(va_status, "vaQueryVideoProcFilters()"))
102         goto error;
103
104     *num_filters_ptr = num_filters;
105     return filters;
106
107 error:
108     g_free(filters);
109     return NULL;
110 }
111
112 static VAProcFilterType *
113 vpp_get_filters(GstVaapiFilter *filter, guint *num_filters_ptr)
114 {
115     VAProcFilterType *filters;
116
117     GST_VAAPI_DISPLAY_LOCK(filter->display);
118     filters = vpp_get_filters_unlocked(filter, num_filters_ptr);
119     GST_VAAPI_DISPLAY_UNLOCK(filter->display);
120     return filters;
121 }
122
123 static gpointer
124 vpp_get_filter_caps_unlocked(
125     GstVaapiFilter *filter, VAProcFilterType type,
126     guint cap_size, guint *num_caps_ptr)
127 {
128     gpointer caps;
129     guint num_caps = 1;
130     VAStatus va_status;
131
132     caps = g_malloc(cap_size);
133     if (!caps)
134         goto error;
135
136     va_status = vaQueryVideoProcFilterCaps(filter->va_display,
137         filter->va_context, type, caps, &num_caps);
138
139     // Try to reallocate to the expected number of filters
140     if (va_status == VA_STATUS_ERROR_MAX_NUM_EXCEEDED) {
141         gpointer const new_caps = g_try_realloc_n(caps, num_caps, cap_size);
142         if (!new_caps)
143             goto error;
144         caps = new_caps;
145
146         va_status = vaQueryVideoProcFilterCaps(filter->va_display,
147             filter->va_context, type, caps, &num_caps);
148     }
149     if (!vaapi_check_status(va_status, "vaQueryVideoProcFilterCaps()"))
150         goto error;
151
152     *num_caps_ptr = num_caps;
153     return caps;
154
155 error:
156     g_free(caps);
157     return NULL;
158 }
159
160 static gpointer
161 vpp_get_filter_caps(GstVaapiFilter *filter, VAProcFilterType type,
162     guint cap_size, guint *num_caps_ptr)
163 {
164     gpointer caps;
165
166     GST_VAAPI_DISPLAY_LOCK(filter->display);
167     caps = vpp_get_filter_caps_unlocked(filter, type, cap_size, num_caps_ptr);
168     GST_VAAPI_DISPLAY_UNLOCK(filter->display);
169     return caps;
170 }
171 #endif
172
173 /* ------------------------------------------------------------------------- */
174 /* --- VPP Operations                                                   --- */
175 /* ------------------------------------------------------------------------- */
176
177 #if USE_VA_VPP
178 #define DEFAULT_FORMAT  GST_VIDEO_FORMAT_UNKNOWN
179
180 enum {
181     PROP_0,
182
183     PROP_FORMAT         = GST_VAAPI_FILTER_OP_FORMAT,
184     PROP_CROP           = GST_VAAPI_FILTER_OP_CROP,
185     PROP_DENOISE        = GST_VAAPI_FILTER_OP_DENOISE,
186     PROP_SHARPEN        = GST_VAAPI_FILTER_OP_SHARPEN,
187     PROP_HUE            = GST_VAAPI_FILTER_OP_HUE,
188     PROP_SATURATION     = GST_VAAPI_FILTER_OP_SATURATION,
189     PROP_BRIGHTNESS     = GST_VAAPI_FILTER_OP_BRIGHTNESS,
190     PROP_CONTRAST       = GST_VAAPI_FILTER_OP_CONTRAST,
191
192     N_PROPERTIES
193 };
194
195 static GParamSpec *g_properties[N_PROPERTIES] = { NULL, };
196 static gsize g_properties_initialized = FALSE;
197
198 static void
199 init_properties(void)
200 {
201     /**
202      * GstVaapiFilter:format:
203      *
204      * The forced output pixel format, expressed as a #GstVideoFormat.
205      */
206     g_properties[PROP_FORMAT] =
207         g_param_spec_enum("format",
208                           "Format",
209                           "The forced output pixel format",
210                           GST_TYPE_VIDEO_FORMAT,
211                           DEFAULT_FORMAT,
212                           G_PARAM_READWRITE);
213
214     /**
215      * GstVaapiFilter:crop-rect:
216      *
217      * The cropping rectangle, expressed as a #GstVaapiRectangle.
218      */
219     g_properties[PROP_CROP] =
220         g_param_spec_boxed("crop-rect",
221                            "Cropping Rectangle",
222                            "The cropping rectangle",
223                            GST_VAAPI_TYPE_RECTANGLE,
224                            G_PARAM_READWRITE);
225
226     /**
227      * GstVaapiFilter:denoise:
228      *
229      * The level of noise reduction to apply.
230      */
231     g_properties[PROP_DENOISE] =
232         g_param_spec_float("denoise",
233                            "Denoising Level",
234                            "The level of denoising to apply",
235                            0.0, 1.0, 0.0,
236                            G_PARAM_READWRITE);
237
238     /**
239      * GstVaapiFilter:sharpen:
240      *
241      * The level of sharpening to apply for positive values, or the
242      * level of blurring for negative values.
243      */
244     g_properties[PROP_SHARPEN] =
245         g_param_spec_float("sharpen",
246                            "Sharpening Level",
247                            "The level of sharpening/blurring to apply",
248                            -1.0, 1.0, 0.0,
249                            G_PARAM_READWRITE);
250
251     /**
252      * GstVaapiFilter:hue:
253      *
254      * The color hue, expressed as a float value. Range is -180.0 to
255      * 180.0. Default value is 0.0 and represents no modification.
256      */
257     g_properties[PROP_HUE] =
258         g_param_spec_float("hue",
259                            "Hue",
260                            "The color hue value",
261                            -180.0, 180.0, 0.0,
262                            G_PARAM_READWRITE);
263
264     /**
265      * GstVaapiFilter:saturation:
266      *
267      * The color saturation, expressed as a float value. Range is 0.0
268      * to 2.0. Default value is 1.0 and represents no modification.
269      */
270     g_properties[PROP_SATURATION] =
271         g_param_spec_float("saturation",
272                            "Saturation",
273                            "The color saturation value",
274                            0.0, 2.0, 1.0,
275                            G_PARAM_READWRITE);
276
277     /**
278      * GstVaapiFilter:brightness:
279      *
280      * The color brightness, expressed as a float value. Range is -1.0
281      * to 1.0. Default value is 0.0 and represents no modification.
282      */
283     g_properties[PROP_BRIGHTNESS] =
284         g_param_spec_float("brightness",
285                            "Brightness",
286                            "The color brightness value",
287                            -1.0, 1.0, 0.0,
288                            G_PARAM_READWRITE);
289
290     /**
291      * GstVaapiFilter:contrast:
292      *
293      * The color contrast, expressed as a float value. Range is 0.0 to
294      * 2.0. Default value is 1.0 and represents no modification.
295      */
296     g_properties[PROP_CONTRAST] =
297         g_param_spec_float("contrast",
298                            "Contrast",
299                            "The color contrast value",
300                            0.0, 2.0, 1.0,
301                            G_PARAM_READWRITE);
302 }
303
304 static void
305 ensure_properties(void)
306 {
307     if (g_once_init_enter(&g_properties_initialized)) {
308         init_properties();
309         g_once_init_leave(&g_properties_initialized, TRUE);
310     }
311 }
312
313 static void
314 op_data_free(GstVaapiFilterOpData *op_data)
315 {
316     g_free(op_data->va_caps);
317     g_slice_free(GstVaapiFilterOpData, op_data);
318 }
319
320 static inline gpointer
321 op_data_new(GstVaapiFilterOp op, GParamSpec *pspec)
322 {
323     GstVaapiFilterOpData *op_data;
324
325     op_data = g_slice_new0(GstVaapiFilterOpData);
326     if (!op_data)
327         return NULL;
328
329     op_data->op         = op;
330     op_data->pspec      = pspec;
331     op_data->ref_count  = 1;
332     op_data->va_buffer  = VA_INVALID_ID;
333
334     switch (op) {
335     case GST_VAAPI_FILTER_OP_FORMAT:
336     case GST_VAAPI_FILTER_OP_CROP:
337         op_data->va_type = VAProcFilterNone;
338         break;
339     case GST_VAAPI_FILTER_OP_DENOISE:
340         op_data->va_type = VAProcFilterNoiseReduction;
341         op_data->va_cap_size = sizeof(VAProcFilterCap);
342         op_data->va_buffer_size = sizeof(VAProcFilterParameterBuffer);
343         break;
344     case GST_VAAPI_FILTER_OP_SHARPEN:
345         op_data->va_type = VAProcFilterSharpening;
346         op_data->va_cap_size = sizeof(VAProcFilterCap);
347         op_data->va_buffer_size = sizeof(VAProcFilterParameterBuffer);
348         break;
349     case GST_VAAPI_FILTER_OP_HUE:
350     case GST_VAAPI_FILTER_OP_SATURATION:
351     case GST_VAAPI_FILTER_OP_BRIGHTNESS:
352     case GST_VAAPI_FILTER_OP_CONTRAST:
353         op_data->va_type = VAProcFilterColorBalance;
354         op_data->va_cap_size = sizeof(VAProcFilterCapColorBalance);
355         op_data->va_buffer_size = sizeof(VAProcFilterParameterBufferColorBalance);
356         break;
357     default:
358         g_assert(0 && "unsupported operation");
359         goto error;
360     }
361     return op_data;
362
363 error:
364     op_data_free(op_data);
365     return NULL;
366 }
367
368 static inline gpointer
369 op_data_ref(gpointer data)
370 {
371     GstVaapiFilterOpData * const op_data = data;
372
373     g_return_val_if_fail(op_data != NULL, NULL);
374
375     g_atomic_int_inc(&op_data->ref_count);
376     return op_data;
377 }
378
379 static void
380 op_data_unref(gpointer data)
381 {
382     GstVaapiFilterOpData * const op_data = data;
383
384     g_return_if_fail(op_data != NULL);
385     g_return_if_fail(op_data->ref_count > 0);
386
387     if (g_atomic_int_dec_and_test(&op_data->ref_count))
388         op_data_free(op_data);
389 }
390
391 /* Ensure capability info is set up for the VA filter we are interested in */
392 static gboolean
393 op_data_ensure_caps(GstVaapiFilterOpData *op_data, gpointer filter_caps,
394     guint num_filter_caps)
395 {
396     guchar *filter_cap = filter_caps;
397     guint i;
398
399     // Find the VA filter cap matching the op info sub-type
400     if (op_data->va_subtype) {
401         for (i = 0; i < num_filter_caps; i++) {
402             /* XXX: sub-type shall always be the first field */
403             if (op_data->va_subtype == *(guint *)filter_cap) {
404                 num_filter_caps = 1;
405                 break;
406             }
407             filter_cap += op_data->va_cap_size;
408         }
409         if (i == num_filter_caps)
410             return FALSE;
411     }
412     op_data->va_caps = g_memdup(filter_cap,
413         op_data->va_cap_size * num_filter_caps);
414     return op_data->va_caps != NULL;
415 }
416
417 /* Scale the filter value wrt. library spec and VA driver spec */
418 static gboolean
419 op_data_get_value_float(GstVaapiFilterOpData *op_data,
420     const VAProcFilterValueRange *range, gfloat value, gfloat *out_value_ptr)
421 {
422     GParamSpecFloat * const pspec = G_PARAM_SPEC_FLOAT(op_data->pspec);
423     gfloat out_value;
424
425     g_return_val_if_fail(range != NULL, FALSE);
426     g_return_val_if_fail(out_value_ptr != NULL, FALSE);
427
428     if (value < pspec->minimum || value > pspec->maximum)
429         return FALSE;
430
431     // Scale wrt. the medium ("default") value
432     out_value = range->default_value;
433     if (value > pspec->default_value)
434         out_value += ((value - pspec->default_value) /
435              (pspec->maximum - pspec->default_value) *
436              (range->max_value - range->default_value));
437     else if (value < pspec->default_value)
438         out_value -= ((pspec->default_value - value) /
439              (pspec->default_value - pspec->minimum) *
440              (range->default_value - range->min_value));
441
442     *out_value_ptr = out_value;
443     return TRUE;
444 }
445
446 /* Get default list of operations supported by the library */
447 static GPtrArray *
448 get_operations_default(void)
449 {
450     GPtrArray *ops;
451     guint i;
452
453     ops = g_ptr_array_new_full(N_PROPERTIES, op_data_unref);
454     if (!ops)
455         return NULL;
456
457     ensure_properties();
458
459     for (i = 0; i < N_PROPERTIES; i++) {
460         GParamSpec * const pspec = g_properties[i];
461         if (!pspec)
462             continue;
463
464         GstVaapiFilterOpData * const op_data = op_data_new(i, pspec);
465         if (!op_data)
466             goto error;
467         g_ptr_array_add(ops, op_data);
468     }
469     return ops;
470
471 error:
472     g_ptr_array_unref(ops);
473     return NULL;
474 }
475
476 /* Get the ordered list of operations, based on VA/VPP queries */
477 static GPtrArray *
478 get_operations_ordered(GstVaapiFilter *filter, GPtrArray *default_ops)
479 {
480     GPtrArray *ops;
481     VAProcFilterType *filters;
482     gpointer filter_caps = NULL;
483     guint i, j, num_filters, num_filter_caps = 0;
484
485     ops = g_ptr_array_new_full(default_ops->len, op_data_unref);
486     if (!ops)
487         return NULL;
488
489     filters = vpp_get_filters(filter, &num_filters);
490     if (!filters)
491         goto error;
492
493     // Append virtual ops first, i.e. those without an associated VA filter
494     for (i = 0; i < default_ops->len; i++) {
495         GstVaapiFilterOpData * const op_data =
496             g_ptr_array_index(default_ops, i);
497         if (op_data->va_type == VAProcFilterNone)
498             g_ptr_array_add(ops, op_data_ref(op_data));
499     }
500
501     // Append ops, while preserving the VA filters ordering
502     for (i = 0; i < num_filters; i++) {
503         const VAProcFilterType va_type = filters[i];
504         if (va_type == VAProcFilterNone)
505             continue;
506
507         for (j = 0; j < default_ops->len; j++) {
508             GstVaapiFilterOpData * const op_data =
509                 g_ptr_array_index(default_ops, j);
510             if (op_data->va_type != va_type)
511                 continue;
512
513             if (!filter_caps) {
514                 filter_caps = vpp_get_filter_caps(filter, va_type,
515                     op_data->va_cap_size, &num_filter_caps);
516                 if (!filter_caps)
517                     goto error;
518             }
519             if (!op_data_ensure_caps(op_data, filter_caps, num_filter_caps))
520                 goto error;
521             g_ptr_array_add(ops, op_data_ref(op_data));
522         }
523         free(filter_caps);
524         filter_caps = NULL;
525     }
526
527     if (filter->operations)
528         g_ptr_array_unref(filter->operations);
529     filter->operations = g_ptr_array_ref(ops);
530
531     g_free(filters);
532     g_ptr_array_unref(default_ops);
533     return ops;
534
535 error:
536     g_free(filter_caps);
537     g_free(filters);
538     g_ptr_array_unref(ops);
539     g_ptr_array_unref(default_ops);
540     return NULL;
541 }
542
543 /* Determine the set of supported VPP operations by the specific
544    filter, or known to this library if filter is NULL */
545 static GPtrArray *
546 ensure_operations(GstVaapiFilter *filter)
547 {
548     GPtrArray *ops;
549
550     if (filter && filter->operations)
551         return g_ptr_array_ref(filter->operations);
552
553     ops = get_operations_default();
554     if (!ops)
555         return NULL;
556     return filter ? get_operations_ordered(filter, ops) : ops;
557 }
558 #endif
559
560 /* Find whether the VPP operation is supported or not */
561 GstVaapiFilterOpData *
562 find_operation(GstVaapiFilter *filter, GstVaapiFilterOp op)
563 {
564     guint i;
565
566     if (!filter->operations)
567         return NULL;
568
569     for (i = 0; i < filter->operations->len; i++) {
570         GstVaapiFilterOpData * const op_data =
571             g_ptr_array_index(filter->operations, i);
572         if (op_data->op == op)
573             return op_data;
574     }
575     return NULL;
576 }
577
578 /* Ensure the operation's VA buffer is allocated */
579 static inline gboolean
580 op_ensure_buffer(GstVaapiFilter *filter, GstVaapiFilterOpData *op_data)
581 {
582     if (G_LIKELY(op_data->va_buffer != VA_INVALID_ID))
583         return TRUE;
584     return vaapi_create_buffer(filter->va_display, filter->va_context,
585         VAProcFilterParameterBufferType, op_data->va_buffer_size, NULL,
586         &op_data->va_buffer, NULL);
587 }
588
589 /* Update a generic filter (float value) */
590 #if USE_VA_VPP
591 static gboolean
592 op_set_generic_unlocked(GstVaapiFilter *filter, GstVaapiFilterOpData *op_data,
593     gfloat value)
594 {
595     VAProcFilterParameterBuffer *buf;
596     VAProcFilterCap *filter_cap;
597     gfloat va_value;
598
599     if (!op_data || !op_ensure_buffer(filter, op_data))
600         return FALSE;
601
602     op_data->is_enabled =
603         (value != G_PARAM_SPEC_FLOAT(op_data->pspec)->default_value);
604     if (!op_data->is_enabled)
605         return TRUE;
606
607     filter_cap = op_data->va_caps;
608     if (!op_data_get_value_float(op_data, &filter_cap->range, value, &va_value))
609         return FALSE;
610
611     buf = vaapi_map_buffer(filter->va_display, op_data->va_buffer);
612     if (!buf)
613         return FALSE;
614
615     buf->type = op_data->va_type;
616     buf->value = va_value;
617     vaapi_unmap_buffer(filter->va_display, op_data->va_buffer, NULL);
618     return TRUE;
619 }
620 #endif
621
622 static inline gboolean
623 op_set_generic(GstVaapiFilter *filter, GstVaapiFilterOpData *op_data,
624     gfloat value)
625 {
626     gboolean success = FALSE;
627
628 #if USE_VA_VPP
629     GST_VAAPI_DISPLAY_LOCK(filter->display);
630     success = op_set_generic_unlocked(filter, op_data, value);
631     GST_VAAPI_DISPLAY_UNLOCK(filter->display);
632 #endif
633     return success;
634 }
635
636 /* Update the color balance filter */
637 #if USE_VA_VPP
638 static gboolean
639 op_set_color_balance_unlocked(GstVaapiFilter *filter,
640     GstVaapiFilterOpData *op_data, gfloat value)
641 {
642     VAProcFilterParameterBufferColorBalance *buf;
643     VAProcFilterCapColorBalance *filter_cap;
644     gfloat va_value;
645
646     if (!op_data || !op_ensure_buffer(filter, op_data))
647         return FALSE;
648
649     op_data->is_enabled =
650         (value != G_PARAM_SPEC_FLOAT(op_data->pspec)->default_value);
651     if (!op_data->is_enabled)
652         return TRUE;
653
654     filter_cap = op_data->va_caps;
655     if (!op_data_get_value_float(op_data, &filter_cap->range, value, &va_value))
656         return FALSE;
657
658     buf = vaapi_map_buffer(filter->va_display, op_data->va_buffer);
659     if (!buf)
660         return FALSE;
661
662     buf->type = op_data->va_type;
663     buf->attrib = op_data->va_subtype;
664     buf->value = va_value;
665     vaapi_unmap_buffer(filter->va_display, op_data->va_buffer, NULL);
666     return TRUE;
667 }
668 #endif
669
670 static inline gboolean
671 op_set_color_balance(GstVaapiFilter *filter, GstVaapiFilterOpData *op_data,
672     gfloat value)
673 {
674     gboolean success = FALSE;
675
676 #if USE_VA_VPP
677     GST_VAAPI_DISPLAY_LOCK(filter->display);
678     success = op_set_color_balance_unlocked(filter, op_data, value);
679     GST_VAAPI_DISPLAY_UNLOCK(filter->display);
680 #endif
681     return success;
682 }
683
684 /* ------------------------------------------------------------------------- */
685 /* --- Surface Formats                                                   --- */
686 /* ------------------------------------------------------------------------- */
687
688 static GArray *
689 ensure_formats(GstVaapiFilter *filter)
690 {
691     VASurfaceAttrib *surface_attribs = NULL;
692     guint i, num_surface_attribs = 0;
693     VAStatus va_status;
694
695     if (G_LIKELY(filter->formats))
696         return filter->formats;
697
698 #if VA_CHECK_VERSION(0,34,0)
699     GST_VAAPI_DISPLAY_LOCK(filter->display);
700     va_status = vaQuerySurfaceAttributes(filter->va_display, filter->va_config,
701         NULL, &num_surface_attribs);
702     GST_VAAPI_DISPLAY_UNLOCK(filter->display);
703     if (!vaapi_check_status(va_status, "vaQuerySurfaceAttributes()"))
704         return NULL;
705
706     surface_attribs = g_malloc(num_surface_attribs * sizeof(*surface_attribs));
707     if (!surface_attribs)
708         return NULL;
709
710     GST_VAAPI_DISPLAY_LOCK(filter->display);
711     va_status = vaQuerySurfaceAttributes(filter->va_display, filter->va_config,
712         surface_attribs, &num_surface_attribs);
713     GST_VAAPI_DISPLAY_UNLOCK(filter->display);
714     if (!vaapi_check_status(va_status, "vaQuerySurfaceAttributes()"))
715         return NULL;
716
717     filter->formats = g_array_sized_new(FALSE, FALSE, sizeof(GstVideoFormat),
718         num_surface_attribs);
719     if (!filter->formats)
720         goto error;
721
722     for (i = 0; i < num_surface_attribs; i++) {
723         const VASurfaceAttrib * const surface_attrib = &surface_attribs[i];
724         GstVideoFormat format;
725
726         if (surface_attrib->type != VASurfaceAttribPixelFormat)
727             continue;
728         if (!(surface_attrib->flags & VA_SURFACE_ATTRIB_SETTABLE))
729             continue;
730
731         format = gst_vaapi_video_format_from_va_fourcc(
732             surface_attrib->value.value.i);
733         if (format == GST_VIDEO_FORMAT_UNKNOWN)
734             continue;
735         g_array_append_val(filter->formats, format);
736     }
737 #endif
738
739     g_free(surface_attribs);
740     return filter->formats;
741
742 error:
743     g_free(surface_attribs);
744     return NULL;
745 }
746
747 static inline gboolean
748 is_special_format(GstVideoFormat format)
749 {
750     return format == GST_VIDEO_FORMAT_UNKNOWN ||
751         format == GST_VIDEO_FORMAT_ENCODED;
752 }
753
754 static gboolean
755 find_format(GstVaapiFilter *filter, GstVideoFormat format)
756 {
757     guint i;
758
759     if (is_special_format(format) || !filter->formats)
760         return FALSE;
761
762     for (i = 0; i < filter->formats->len; i++) {
763         if (g_array_index(filter->formats, GstVideoFormat, i) == format)
764             return TRUE;
765     }
766     return FALSE;
767 }
768
769 /* ------------------------------------------------------------------------- */
770 /* --- Interface                                                         --- */
771 /* ------------------------------------------------------------------------- */
772
773 #if USE_VA_VPP
774 static gboolean
775 gst_vaapi_filter_init(GstVaapiFilter *filter, GstVaapiDisplay *display)
776 {
777     VAStatus va_status;
778
779     filter->display     = gst_vaapi_display_ref(display);
780     filter->va_display  = GST_VAAPI_DISPLAY_VADISPLAY(display);
781     filter->va_config   = VA_INVALID_ID;
782     filter->va_context  = VA_INVALID_ID;
783     filter->format      = DEFAULT_FORMAT;
784
785     if (!GST_VAAPI_DISPLAY_HAS_VPP(display))
786         return FALSE;
787
788     va_status = vaCreateConfig(filter->va_display, VAProfileNone,
789         VAEntrypointVideoProc, NULL, 0, &filter->va_config);
790     if (!vaapi_check_status(va_status, "vaCreateConfig() [VPP]"))
791         return FALSE;
792
793     va_status = vaCreateContext(filter->va_display, filter->va_config, 0, 0, 0,
794         NULL, 0, &filter->va_context);
795     if (!vaapi_check_status(va_status, "vaCreateContext() [VPP]"))
796         return FALSE;
797     return TRUE;
798 }
799
800 static void
801 gst_vaapi_filter_finalize(GstVaapiFilter *filter)
802 {
803     guint i;
804
805     GST_VAAPI_DISPLAY_LOCK(filter->display);
806     if (filter->operations) {
807         for (i = 0; i < filter->operations->len; i++) {
808             GstVaapiFilterOpData * const op_data =
809                 g_ptr_array_index(filter->operations, i);
810             vaapi_destroy_buffer(filter->va_display, &op_data->va_buffer);
811         }
812         g_ptr_array_unref(filter->operations);
813         filter->operations = NULL;
814     }
815
816     if (filter->va_context != VA_INVALID_ID) {
817         vaDestroyContext(filter->va_display, filter->va_context);
818         filter->va_context = VA_INVALID_ID;
819     }
820
821     if (filter->va_config != VA_INVALID_ID) {
822         vaDestroyConfig(filter->va_display, filter->va_config);
823         filter->va_config = VA_INVALID_ID;
824     }
825     GST_VAAPI_DISPLAY_UNLOCK(filter->display);
826     gst_vaapi_display_replace(&filter->display, NULL);
827
828     if (filter->formats) {
829         g_array_unref(filter->formats);
830         filter->formats = NULL;
831     }
832 }
833
834 static inline const GstVaapiMiniObjectClass *
835 gst_vaapi_filter_class(void)
836 {
837     static const GstVaapiMiniObjectClass GstVaapiFilterClass = {
838         sizeof(GstVaapiFilter),
839         (GDestroyNotify)gst_vaapi_filter_finalize
840     };
841     return &GstVaapiFilterClass;
842 }
843 #endif
844
845 /**
846  * gst_vaapi_filter_new:
847  * @display: a #GstVaapiDisplay
848  *
849  * Creates a new #GstVaapiFilter set up to operate in "identity"
850  * mode. This means that no other operation than scaling is performed.
851  *
852  * Return value: the newly created #GstVaapiFilter object
853  */
854 GstVaapiFilter *
855 gst_vaapi_filter_new(GstVaapiDisplay *display)
856 {
857 #if USE_VA_VPP
858     GstVaapiFilter *filter;
859
860     filter = (GstVaapiFilter *)
861         gst_vaapi_mini_object_new0(gst_vaapi_filter_class());
862     if (!filter)
863         return NULL;
864
865     if (!gst_vaapi_filter_init(filter, display))
866         goto error;
867     return filter;
868
869 error:
870     gst_vaapi_filter_unref(filter);
871     return NULL;
872 #else
873     GST_WARNING("video processing is not supported, "
874                 "please consider an upgrade to VA-API >= 0.34");
875     return NULL;
876 #endif
877 }
878
879 /**
880  * gst_vaapi_filter_ref:
881  * @filter: a #GstVaapiFilter
882  *
883  * Atomically increases the reference count of the given @filter by one.
884  *
885  * Returns: The same @filter argument
886  */
887 GstVaapiFilter *
888 gst_vaapi_filter_ref(GstVaapiFilter *filter)
889 {
890     g_return_val_if_fail(filter != NULL, NULL);
891
892     return GST_VAAPI_FILTER(gst_vaapi_mini_object_ref(
893                                 GST_VAAPI_MINI_OBJECT(filter)));
894 }
895
896 /**
897  * gst_vaapi_filter_unref:
898  * @filter: a #GstVaapiFilter
899  *
900  * Atomically decreases the reference count of the @filter by one. If
901  * the reference count reaches zero, the filter will be free'd.
902  */
903 void
904 gst_vaapi_filter_unref(GstVaapiFilter *filter)
905 {
906     g_return_if_fail(filter != NULL);
907
908     gst_vaapi_mini_object_unref(GST_VAAPI_MINI_OBJECT(filter));
909 }
910
911 /**
912  * gst_vaapi_filter_replace:
913  * @old_filter_ptr: a pointer to a #GstVaapiFilter
914  * @new_filter: a #GstVaapiFilter
915  *
916  * Atomically replaces the filter held in @old_filter_ptr with
917  * @new_filter. This means that @old_filter_ptr shall reference a
918  * valid filter. However, @new_filter can be NULL.
919  */
920 void
921 gst_vaapi_filter_replace(GstVaapiFilter **old_filter_ptr,
922     GstVaapiFilter *new_filter)
923 {
924     g_return_if_fail(old_filter_ptr != NULL);
925
926     gst_vaapi_mini_object_replace((GstVaapiMiniObject **)old_filter_ptr,
927         GST_VAAPI_MINI_OBJECT(new_filter));
928 }
929
930 /**
931  * gst_vaapi_filter_get_operations:
932  * @filter: a #GstVaapiFilter, or %NULL
933  *
934  * Determines the set of supported operations for video processing.
935  * The caller owns an extra reference to the resulting array of
936  * #GstVaapiFilterOpInfo elements, so it shall be released with
937  * g_ptr_array_unref() after usage.
938  *
939  * If @filter is %NULL, then this function returns the video
940  * processing operations supported by this library.
941  *
942  * Return value: the set of supported operations, or %NULL if an error
943  *   occurred.
944  */
945 GPtrArray *
946 gst_vaapi_filter_get_operations(GstVaapiFilter *filter)
947 {
948 #if USE_VA_VPP
949     return ensure_operations(filter);
950 #else
951     return NULL;
952 #endif
953 }
954
955 /**
956  * gst_vaapi_filter_set_operation:
957  * @filter: a #GstVaapiFilter
958  * @op: a #GstVaapiFilterOp
959  * @value: the @op settings
960  *
961  * Enable the specified operation @op to be performed during video
962  * processing, i.e. in gst_vaapi_filter_process(). The @value argument
963  * specifies the operation settings. e.g. deinterlacing method for
964  * deinterlacing, denoising level for noise reduction, etc.
965  *
966  * If @value is %NULL, then this function resets the operation
967  * settings to their default values.
968  *
969  * Return value: %TRUE if the specified operation may be supported,
970  *   %FALSE otherwise
971  */
972 gboolean
973 gst_vaapi_filter_set_operation(GstVaapiFilter *filter, GstVaapiFilterOp op,
974     const GValue *value)
975 {
976 #if USE_VA_VPP
977     GstVaapiFilterOpData *op_data;
978
979     g_return_val_if_fail(filter != NULL, FALSE);
980
981     op_data = find_operation(filter, op);
982     if (!op_data)
983         return FALSE;
984
985     if (value && !G_VALUE_HOLDS(value, G_PARAM_SPEC_VALUE_TYPE(op_data->pspec)))
986         return FALSE;
987
988     switch (op) {
989     case GST_VAAPI_FILTER_OP_FORMAT:
990         return gst_vaapi_filter_set_format(filter, value ?
991             g_value_get_enum(value) : DEFAULT_FORMAT);
992     case GST_VAAPI_FILTER_OP_CROP:
993         return gst_vaapi_filter_set_cropping_rectangle(filter, value ?
994             g_value_get_boxed(value) : NULL);
995     case GST_VAAPI_FILTER_OP_DENOISE:
996     case GST_VAAPI_FILTER_OP_SHARPEN:
997         return op_set_generic(filter, op_data,
998             (value ? g_value_get_float(value) :
999              G_PARAM_SPEC_FLOAT(op_data->pspec)->default_value));
1000     case GST_VAAPI_FILTER_OP_HUE:
1001     case GST_VAAPI_FILTER_OP_SATURATION:
1002     case GST_VAAPI_FILTER_OP_BRIGHTNESS:
1003     case GST_VAAPI_FILTER_OP_CONTRAST:
1004         return op_set_color_balance(filter, op_data,
1005             (value ? g_value_get_float(value) :
1006              G_PARAM_SPEC_FLOAT(op_data->pspec)->default_value));
1007     default:
1008         break;
1009     }
1010 #endif
1011     return FALSE;
1012 }
1013
1014 /**
1015  * gst_vaapi_filter_process:
1016  * @filter: a #GstVaapiFilter
1017  * @src_surface: the source @GstVaapiSurface
1018  * @dst_surface: the destination @GstVaapiSurface
1019  * @flags: #GstVaapiSurfaceRenderFlags that apply to @src_surface
1020  *
1021  * Applies the operations currently defined in the @filter to
1022  * @src_surface and return the output in @dst_surface. The order of
1023  * operations is determined in a way that suits best the underlying
1024  * hardware. i.e. the only guarantee held is the generated outcome,
1025  * not any specific order of operations.
1026  *
1027  * Return value: a #GstVaapiFilterStatus
1028  */
1029 static GstVaapiFilterStatus
1030 gst_vaapi_filter_process_unlocked(GstVaapiFilter *filter,
1031     GstVaapiSurface *src_surface, GstVaapiSurface *dst_surface, guint flags)
1032 {
1033 #if USE_VA_VPP
1034     VAProcPipelineParameterBuffer *pipeline_param = NULL;
1035     VABufferID pipeline_param_buf_id;
1036     VABufferID filters[N_PROPERTIES];
1037     guint i, num_filters = 0;
1038     VAStatus va_status;
1039     VARectangle src_rect, dst_rect;
1040
1041     if (!ensure_operations(filter))
1042         return GST_VAAPI_FILTER_STATUS_ERROR_ALLOCATION_FAILED;
1043
1044     if (filter->use_crop_rect) {
1045         const GstVaapiRectangle * const crop_rect = &filter->crop_rect;
1046
1047         if ((crop_rect->x + crop_rect->width >
1048              GST_VAAPI_SURFACE_WIDTH(src_surface)) ||
1049             (crop_rect->y + crop_rect->height >
1050              GST_VAAPI_SURFACE_HEIGHT(src_surface)))
1051             goto error;
1052
1053         src_rect.x      = crop_rect->x;
1054         src_rect.y      = crop_rect->y;
1055         src_rect.width  = crop_rect->width;
1056         src_rect.height = crop_rect->height;
1057     }
1058     else {
1059         src_rect.x      = 0;
1060         src_rect.y      = 0;
1061         src_rect.width  = GST_VAAPI_SURFACE_WIDTH(src_surface);
1062         src_rect.height = GST_VAAPI_SURFACE_HEIGHT(src_surface);
1063     }
1064
1065     dst_rect.x      = 0;
1066     dst_rect.y      = 0;
1067     dst_rect.width  = GST_VAAPI_SURFACE_WIDTH(dst_surface);
1068     dst_rect.height = GST_VAAPI_SURFACE_HEIGHT(dst_surface);
1069
1070     for (i = 0, num_filters = 0; i < filter->operations->len; i++) {
1071         GstVaapiFilterOpData * const op_data =
1072             g_ptr_array_index(filter->operations, i);
1073         if (!op_data->is_enabled)
1074             continue;
1075         if (op_data->va_buffer == VA_INVALID_ID) {
1076             GST_ERROR("invalid VA buffer for operation %s",
1077                       g_param_spec_get_name(op_data->pspec));
1078             goto error;
1079         }
1080         filters[num_filters++] = op_data->va_buffer;
1081     }
1082
1083     if (!vaapi_create_buffer(filter->va_display, filter->va_context,
1084             VAProcPipelineParameterBufferType, sizeof(*pipeline_param),
1085             NULL, &pipeline_param_buf_id, (gpointer *)&pipeline_param))
1086         goto error;
1087
1088     memset(pipeline_param, 0, sizeof(*pipeline_param));
1089     pipeline_param->surface = GST_VAAPI_OBJECT_ID(src_surface);
1090     pipeline_param->surface_region = &src_rect;
1091     pipeline_param->surface_color_standard = VAProcColorStandardNone;
1092     pipeline_param->output_region = &dst_rect;
1093     pipeline_param->output_color_standard = VAProcColorStandardNone;
1094     pipeline_param->output_background_color = 0xff000000;
1095     pipeline_param->filter_flags = from_GstVaapiSurfaceRenderFlags(flags);
1096     pipeline_param->filters = filters;
1097     pipeline_param->num_filters = num_filters;
1098
1099     vaapi_unmap_buffer(filter->va_display, pipeline_param_buf_id, NULL);
1100
1101     va_status = vaBeginPicture(filter->va_display, filter->va_context,
1102         GST_VAAPI_OBJECT_ID(dst_surface));
1103     if (!vaapi_check_status(va_status, "vaBeginPicture()"))
1104         goto error;
1105
1106     va_status = vaRenderPicture(filter->va_display, filter->va_context,
1107         &pipeline_param_buf_id, 1);
1108     if (!vaapi_check_status(va_status, "vaRenderPicture()"))
1109         goto error;
1110
1111     va_status = vaEndPicture(filter->va_display, filter->va_context);
1112     if (!vaapi_check_status(va_status, "vaEndPicture()"))
1113         goto error;
1114     return GST_VAAPI_FILTER_STATUS_SUCCESS;
1115
1116 error:
1117     vaDestroyBuffer(filter->va_display, pipeline_param_buf_id);
1118     return GST_VAAPI_FILTER_STATUS_ERROR_OPERATION_FAILED;
1119 #endif
1120     return GST_VAAPI_FILTER_STATUS_ERROR_UNSUPPORTED_OPERATION;
1121 }
1122
1123 GstVaapiFilterStatus
1124 gst_vaapi_filter_process(GstVaapiFilter *filter, GstVaapiSurface *src_surface,
1125     GstVaapiSurface *dst_surface, guint flags)
1126 {
1127     GstVaapiFilterStatus status;
1128
1129     g_return_val_if_fail(filter != NULL,
1130         GST_VAAPI_FILTER_STATUS_ERROR_INVALID_PARAMETER);
1131     g_return_val_if_fail(src_surface != NULL,
1132         GST_VAAPI_FILTER_STATUS_ERROR_INVALID_PARAMETER);
1133     g_return_val_if_fail(dst_surface != NULL,
1134         GST_VAAPI_FILTER_STATUS_ERROR_INVALID_PARAMETER);
1135
1136     GST_VAAPI_DISPLAY_LOCK(filter->display);
1137     status = gst_vaapi_filter_process_unlocked(filter,
1138         src_surface, dst_surface, flags);
1139     GST_VAAPI_DISPLAY_UNLOCK(filter->display);
1140     return status;
1141 }
1142
1143 /**
1144  * gst_vaapi_filter_get_formats:
1145  * @filter: a #GstVaapiFilter
1146  *
1147  * Determines the set of supported source or target formats for video
1148  * processing.  The caller owns an extra reference to the resulting
1149  * array of #GstVideoFormat elements, so it shall be released with
1150  * g_array_unref() after usage.
1151  *
1152  * Return value: the set of supported target formats for video processing.
1153  */
1154 GArray *
1155 gst_vaapi_filter_get_formats(GstVaapiFilter *filter)
1156 {
1157     g_return_val_if_fail(filter != NULL, NULL);
1158
1159     return ensure_formats(filter);
1160 }
1161
1162 /**
1163  * gst_vaapi_filter_set_format:
1164  * @filter: a #GstVaapiFilter
1165  * @format: the target surface format
1166  *
1167  * Sets the desired pixel format of the resulting video processing
1168  * operations.
1169  *
1170  * If @format is #GST_VIDEO_FORMAT_UNKNOWN, the filter will assume iso
1171  * format conversion, i.e. no color conversion at all and the target
1172  * surface format shall match the source surface format.
1173  *
1174  * If @format is #GST_VIDEO_FORMAT_ENCODED, the filter will use the pixel
1175  * format of the target surface passed to gst_vaapi_filter_process().
1176  *
1177  * Return value: %TRUE if the color conversion to the specified @format
1178  *   may be supported, %FALSE otherwise.
1179  */
1180 gboolean
1181 gst_vaapi_filter_set_format(GstVaapiFilter *filter, GstVideoFormat format)
1182 {
1183     g_return_val_if_fail(filter != NULL, FALSE);
1184
1185     if (!ensure_formats(filter))
1186         return FALSE;
1187
1188     if (!is_special_format(format) && !find_format(filter, format))
1189         return FALSE;
1190
1191     filter->format = format;
1192     return TRUE;
1193 }
1194
1195 /**
1196  * gst_vaapi_filter_set_cropping_rectangle:
1197  * @filter: a #GstVaapiFilter
1198  * @rect: the cropping region
1199  *
1200  * Sets the source surface cropping rectangle to use during the video
1201  * processing. If @rect is %NULL, the whole source surface will be used.
1202  *
1203  * Return value: %TRUE if the operation is supported, %FALSE otherwise.
1204  */
1205 gboolean
1206 gst_vaapi_filter_set_cropping_rectangle(GstVaapiFilter *filter,
1207     const GstVaapiRectangle *rect)
1208 {
1209     g_return_val_if_fail(filter != NULL, FALSE);
1210
1211     filter->use_crop_rect = rect != NULL;
1212     if (filter->use_crop_rect)
1213         filter->crop_rect = *rect;
1214     return TRUE;
1215 }
1216
1217 /**
1218  * gst_vaapi_filter_set_denoising_level:
1219  * @filter: a #GstVaapiFilter
1220  * @level: the level of noise reduction to apply
1221  *
1222  * Sets the noise reduction level to apply. If @level is 0.0f, this
1223  * corresponds to disabling the noise reduction algorithm.
1224  *
1225  * Return value: %TRUE if the operation is supported, %FALSE otherwise.
1226  */
1227 gboolean
1228 gst_vaapi_filter_set_denoising_level(GstVaapiFilter *filter, gfloat level)
1229 {
1230     g_return_val_if_fail(filter != NULL, FALSE);
1231
1232     return op_set_generic(filter,
1233         find_operation(filter, GST_VAAPI_FILTER_OP_DENOISE), level);
1234 }
1235
1236 /**
1237  * gst_vaapi_filter_set_sharpening_level:
1238  * @filter: a #GstVaapiFilter
1239  * @level: the sharpening factor
1240  *
1241  * Enables noise reduction with the specified factor.
1242  *
1243  * Return value: %TRUE if the operation is supported, %FALSE otherwise.
1244  */
1245 gboolean
1246 gst_vaapi_filter_set_sharpening_level(GstVaapiFilter *filter, gfloat level)
1247 {
1248     g_return_val_if_fail(filter != NULL, FALSE);
1249
1250     return op_set_generic(filter,
1251         find_operation(filter, GST_VAAPI_FILTER_OP_SHARPEN), level);
1252 }
1253
1254 /**
1255  * gst_vaapi_filter_set_hue:
1256  * @filter: a #GstVaapiFilter
1257  * @value: the color hue value
1258  *
1259  * Enables color hue adjustment to the specified value.
1260  *
1261  * Return value: %TRUE if the operation is supported, %FALSE otherwise.
1262  */
1263 gboolean
1264 gst_vaapi_filter_set_hue(GstVaapiFilter *filter, gfloat value)
1265 {
1266     g_return_val_if_fail(filter != NULL, FALSE);
1267
1268     return op_set_color_balance(filter,
1269         find_operation(filter, GST_VAAPI_FILTER_OP_HUE), value);
1270 }
1271
1272 /**
1273  * gst_vaapi_filter_set_saturation:
1274  * @filter: a #GstVaapiFilter
1275  * @value: the color saturation value
1276  *
1277  * Enables color saturation adjustment to the specified value.
1278  *
1279  * Return value: %TRUE if the operation is supported, %FALSE otherwise.
1280  */
1281 gboolean
1282 gst_vaapi_filter_set_saturation(GstVaapiFilter *filter, gfloat value)
1283 {
1284     g_return_val_if_fail(filter != NULL, FALSE);
1285
1286     return op_set_color_balance(filter,
1287         find_operation(filter, GST_VAAPI_FILTER_OP_SATURATION), value);
1288 }
1289
1290 /**
1291  * gst_vaapi_filter_set_brightness:
1292  * @filter: a #GstVaapiFilter
1293  * @value: the color brightness value
1294  *
1295  * Enables color brightness adjustment to the specified value.
1296  *
1297  * Return value: %TRUE if the operation is supported, %FALSE otherwise.
1298  */
1299 gboolean
1300 gst_vaapi_filter_set_brightness(GstVaapiFilter *filter, gfloat value)
1301 {
1302     g_return_val_if_fail(filter != NULL, FALSE);
1303
1304     return op_set_color_balance(filter,
1305         find_operation(filter, GST_VAAPI_FILTER_OP_BRIGHTNESS), value);
1306 }
1307
1308 /**
1309  * gst_vaapi_filter_set_contrast:
1310  * @filter: a #GstVaapiFilter
1311  * @value: the color contrast value
1312  *
1313  * Enables color contrast adjustment to the specified value.
1314  *
1315  * Return value: %TRUE if the operation is supported, %FALSE otherwise.
1316  */
1317 gboolean
1318 gst_vaapi_filter_set_contrast(GstVaapiFilter *filter, gfloat value)
1319 {
1320     g_return_val_if_fail(filter != NULL, FALSE);
1321
1322     return op_set_color_balance(filter,
1323         find_operation(filter, GST_VAAPI_FILTER_OP_CONTRAST), value);
1324 }