c201643563abcb95ff4e4feb82c5d9de1d6f406c
[platform/upstream/gstreamer-vaapi.git] / 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 "gstvaapifilter.h"
25 #include "gstvaapiutils.h"
26 #include "gstvaapivalue.h"
27 #include "gstvaapiminiobject.h"
28 #include "gstvaapidisplay_priv.h"
29 #include "gstvaapisurface_priv.h"
30
31 #if USE_VA_VPP
32 # include <va/va_vpp.h>
33 #endif
34
35 #define DEBUG 1
36 #include "gstvaapidebug.h"
37
38 #define GST_VAAPI_FILTER(obj) \
39     ((GstVaapiFilter *)(obj))
40
41 typedef struct _GstVaapiFilterOpData GstVaapiFilterOpData;
42 struct _GstVaapiFilterOpData {
43     GstVaapiFilterOp    op;
44     GParamSpec         *pspec;
45     volatile gint       ref_count;
46     guint               va_type;
47     guint               va_subtype;
48     gpointer            va_caps;
49     guint               va_num_caps;
50     guint               va_cap_size;
51     VABufferID          va_buffer;
52     guint               va_buffer_size;
53     guint               is_enabled      : 1;
54 };
55
56 struct _GstVaapiFilter {
57     /*< private >*/
58     GstVaapiMiniObject  parent_instance;
59
60     GstVaapiDisplay    *display;
61     VADisplay           va_display;
62     VAConfigID          va_config;
63     VAContextID         va_context;
64     GPtrArray          *operations;
65     GstVideoFormat      format;
66     GArray             *formats;
67     GArray             *forward_references;
68     GArray             *backward_references;
69     GstVaapiRectangle   crop_rect;
70     GstVaapiRectangle   target_rect;
71     guint               use_crop_rect   : 1;
72     guint               use_target_rect : 1;
73 };
74
75 /* ------------------------------------------------------------------------- */
76 /* --- VPP Types                                                         --- */
77 /* ------------------------------------------------------------------------- */
78
79 GType
80 gst_vaapi_deinterlace_method_get_type(void)
81 {
82     static gsize g_type = 0;
83
84     static const GEnumValue enum_values[] = {
85         { GST_VAAPI_DEINTERLACE_METHOD_NONE,
86           "Disable deinterlacing", "none" },
87         { GST_VAAPI_DEINTERLACE_METHOD_BOB,
88           "Bob deinterlacing", "bob" },
89 #if USE_VA_VPP
90         { GST_VAAPI_DEINTERLACE_METHOD_WEAVE,
91           "Weave deinterlacing", "weave" },
92         { GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE,
93           "Motion adaptive deinterlacing", "motion-adaptive" },
94         { GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED,
95           "Motion compensated deinterlacing", "motion-compensated" },
96 #endif
97         { 0, NULL, NULL },
98     };
99
100     if (g_once_init_enter(&g_type)) {
101         const GType type =
102             g_enum_register_static("GstVaapiDeinterlaceMethod", enum_values);
103         g_once_init_leave(&g_type, type);
104     }
105     return g_type;
106 }
107
108 GType
109 gst_vaapi_deinterlace_flags_get_type(void)
110 {
111     static gsize g_type = 0;
112
113     static const GEnumValue enum_values[] = {
114         { GST_VAAPI_DEINTERLACE_FLAG_TFF,
115           "Top-field first", "top-field-first" },
116         { GST_VAAPI_DEINTERLACE_FLAG_ONEFIELD,
117           "One field", "one-field" },
118         { GST_VAAPI_DEINTERLACE_FLAG_TOPFIELD,
119           "Top field", "top-field" },
120         { 0, NULL, NULL }
121     };
122
123     if (g_once_init_enter(&g_type)) {
124         const GType type =
125             g_enum_register_static("GstVaapiDeinterlaceFlags", enum_values);
126         g_once_init_leave(&g_type, type);
127     }
128     return g_type;
129 }
130
131 /* ------------------------------------------------------------------------- */
132 /* --- VPP Helpers                                                       --- */
133 /* ------------------------------------------------------------------------- */
134
135 #if USE_VA_VPP
136 static VAProcFilterType *
137 vpp_get_filters_unlocked(GstVaapiFilter *filter, guint *num_filters_ptr)
138 {
139     VAProcFilterType *filters = NULL;
140     guint num_filters = 0;
141     VAStatus va_status;
142
143     num_filters = VAProcFilterCount;
144     filters = g_malloc_n(num_filters, sizeof(*filters));
145     if (!filters)
146         goto error;
147
148     va_status = vaQueryVideoProcFilters(filter->va_display, filter->va_context,
149         filters, &num_filters);
150
151     // Try to reallocate to the expected number of filters
152     if (va_status == VA_STATUS_ERROR_MAX_NUM_EXCEEDED) {
153         VAProcFilterType * const new_filters =
154             g_try_realloc_n(filters, num_filters, sizeof(*new_filters));
155         if (!new_filters)
156             goto error;
157         filters = new_filters;
158
159         va_status = vaQueryVideoProcFilters(filter->va_display,
160             filter->va_context, filters, &num_filters);
161     }
162     if (!vaapi_check_status(va_status, "vaQueryVideoProcFilters()"))
163         goto error;
164
165     *num_filters_ptr = num_filters;
166     return filters;
167
168 error:
169     g_free(filters);
170     return NULL;
171 }
172
173 static VAProcFilterType *
174 vpp_get_filters(GstVaapiFilter *filter, guint *num_filters_ptr)
175 {
176     VAProcFilterType *filters;
177
178     GST_VAAPI_DISPLAY_LOCK(filter->display);
179     filters = vpp_get_filters_unlocked(filter, num_filters_ptr);
180     GST_VAAPI_DISPLAY_UNLOCK(filter->display);
181     return filters;
182 }
183
184 static gpointer
185 vpp_get_filter_caps_unlocked(
186     GstVaapiFilter *filter, VAProcFilterType type,
187     guint cap_size, guint *num_caps_ptr)
188 {
189     gpointer caps;
190     guint num_caps = 1;
191     VAStatus va_status;
192
193     caps = g_malloc(cap_size);
194     if (!caps)
195         goto error;
196
197     va_status = vaQueryVideoProcFilterCaps(filter->va_display,
198         filter->va_context, type, caps, &num_caps);
199
200     // Try to reallocate to the expected number of filters
201     if (va_status == VA_STATUS_ERROR_MAX_NUM_EXCEEDED) {
202         gpointer const new_caps = g_try_realloc_n(caps, num_caps, cap_size);
203         if (!new_caps)
204             goto error;
205         caps = new_caps;
206
207         va_status = vaQueryVideoProcFilterCaps(filter->va_display,
208             filter->va_context, type, caps, &num_caps);
209     }
210     if (!vaapi_check_status(va_status, "vaQueryVideoProcFilterCaps()"))
211         goto error;
212
213     *num_caps_ptr = num_caps;
214     return caps;
215
216 error:
217     g_free(caps);
218     return NULL;
219 }
220
221 static gpointer
222 vpp_get_filter_caps(GstVaapiFilter *filter, VAProcFilterType type,
223     guint cap_size, guint *num_caps_ptr)
224 {
225     gpointer caps;
226
227     GST_VAAPI_DISPLAY_LOCK(filter->display);
228     caps = vpp_get_filter_caps_unlocked(filter, type, cap_size, num_caps_ptr);
229     GST_VAAPI_DISPLAY_UNLOCK(filter->display);
230     return caps;
231 }
232 #endif
233
234 /* ------------------------------------------------------------------------- */
235 /* --- VPP Operations                                                   --- */
236 /* ------------------------------------------------------------------------- */
237
238 #if USE_VA_VPP
239 #define DEFAULT_FORMAT  GST_VIDEO_FORMAT_UNKNOWN
240
241 enum {
242     PROP_0,
243
244     PROP_FORMAT         = GST_VAAPI_FILTER_OP_FORMAT,
245     PROP_CROP           = GST_VAAPI_FILTER_OP_CROP,
246     PROP_DENOISE        = GST_VAAPI_FILTER_OP_DENOISE,
247     PROP_SHARPEN        = GST_VAAPI_FILTER_OP_SHARPEN,
248     PROP_HUE            = GST_VAAPI_FILTER_OP_HUE,
249     PROP_SATURATION     = GST_VAAPI_FILTER_OP_SATURATION,
250     PROP_BRIGHTNESS     = GST_VAAPI_FILTER_OP_BRIGHTNESS,
251     PROP_CONTRAST       = GST_VAAPI_FILTER_OP_CONTRAST,
252     PROP_DEINTERLACING  = GST_VAAPI_FILTER_OP_DEINTERLACING,
253
254     N_PROPERTIES
255 };
256
257 static GParamSpec *g_properties[N_PROPERTIES] = { NULL, };
258 static gsize g_properties_initialized = FALSE;
259
260 static void
261 init_properties(void)
262 {
263     /**
264      * GstVaapiFilter:format:
265      *
266      * The forced output pixel format, expressed as a #GstVideoFormat.
267      */
268     g_properties[PROP_FORMAT] =
269         g_param_spec_enum("format",
270                           "Format",
271                           "The forced output pixel format",
272                           GST_TYPE_VIDEO_FORMAT,
273                           DEFAULT_FORMAT,
274                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
275
276     /**
277      * GstVaapiFilter:crop-rect:
278      *
279      * The cropping rectangle, expressed as a #GstVaapiRectangle.
280      */
281     g_properties[PROP_CROP] =
282         g_param_spec_boxed("crop-rect",
283                            "Cropping Rectangle",
284                            "The cropping rectangle",
285                            GST_VAAPI_TYPE_RECTANGLE,
286                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
287
288     /**
289      * GstVaapiFilter:denoise:
290      *
291      * The level of noise reduction to apply.
292      */
293     g_properties[PROP_DENOISE] =
294         g_param_spec_float("denoise",
295                            "Denoising Level",
296                            "The level of denoising to apply",
297                            0.0, 1.0, 0.0,
298                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
299
300     /**
301      * GstVaapiFilter:sharpen:
302      *
303      * The level of sharpening to apply for positive values, or the
304      * level of blurring for negative values.
305      */
306     g_properties[PROP_SHARPEN] =
307         g_param_spec_float("sharpen",
308                            "Sharpening Level",
309                            "The level of sharpening/blurring to apply",
310                            -1.0, 1.0, 0.0,
311                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
312
313     /**
314      * GstVaapiFilter:hue:
315      *
316      * The color hue, expressed as a float value. Range is -180.0 to
317      * 180.0. Default value is 0.0 and represents no modification.
318      */
319     g_properties[PROP_HUE] =
320         g_param_spec_float("hue",
321                            "Hue",
322                            "The color hue value",
323                            -180.0, 180.0, 0.0,
324                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
325
326     /**
327      * GstVaapiFilter:saturation:
328      *
329      * The color saturation, expressed as a float value. Range is 0.0
330      * to 2.0. Default value is 1.0 and represents no modification.
331      */
332     g_properties[PROP_SATURATION] =
333         g_param_spec_float("saturation",
334                            "Saturation",
335                            "The color saturation value",
336                            0.0, 2.0, 1.0,
337                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
338
339     /**
340      * GstVaapiFilter:brightness:
341      *
342      * The color brightness, expressed as a float value. Range is -1.0
343      * to 1.0. Default value is 0.0 and represents no modification.
344      */
345     g_properties[PROP_BRIGHTNESS] =
346         g_param_spec_float("brightness",
347                            "Brightness",
348                            "The color brightness value",
349                            -1.0, 1.0, 0.0,
350                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
351
352     /**
353      * GstVaapiFilter:contrast:
354      *
355      * The color contrast, expressed as a float value. Range is 0.0 to
356      * 2.0. Default value is 1.0 and represents no modification.
357      */
358     g_properties[PROP_CONTRAST] =
359         g_param_spec_float("contrast",
360                            "Contrast",
361                            "The color contrast value",
362                            0.0, 2.0, 1.0,
363                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
364
365     /**
366      * GstVaapiFilter:deinterlace-method:
367      *
368      * The deinterlacing algorithm to apply, expressed a an enum
369      * value. See #GstVaapiDeinterlaceMethod.
370      */
371     g_properties[PROP_DEINTERLACING] =
372         g_param_spec_enum("deinterlace",
373                           "Deinterlacing Method",
374                           "Deinterlacing method to apply",
375                           GST_VAAPI_TYPE_DEINTERLACE_METHOD,
376                           GST_VAAPI_DEINTERLACE_METHOD_NONE,
377                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
378 }
379
380 static void
381 ensure_properties(void)
382 {
383     if (g_once_init_enter(&g_properties_initialized)) {
384         init_properties();
385         g_once_init_leave(&g_properties_initialized, TRUE);
386     }
387 }
388
389 static void
390 op_data_free(GstVaapiFilterOpData *op_data)
391 {
392     g_free(op_data->va_caps);
393     g_slice_free(GstVaapiFilterOpData, op_data);
394 }
395
396 static inline gpointer
397 op_data_new(GstVaapiFilterOp op, GParamSpec *pspec)
398 {
399     GstVaapiFilterOpData *op_data;
400
401     op_data = g_slice_new0(GstVaapiFilterOpData);
402     if (!op_data)
403         return NULL;
404
405     op_data->op         = op;
406     op_data->pspec      = pspec;
407     op_data->ref_count  = 1;
408     op_data->va_buffer  = VA_INVALID_ID;
409
410     switch (op) {
411     case GST_VAAPI_FILTER_OP_FORMAT:
412     case GST_VAAPI_FILTER_OP_CROP:
413         op_data->va_type = VAProcFilterNone;
414         break;
415     case GST_VAAPI_FILTER_OP_DENOISE:
416         op_data->va_type = VAProcFilterNoiseReduction;
417         op_data->va_cap_size = sizeof(VAProcFilterCap);
418         op_data->va_buffer_size = sizeof(VAProcFilterParameterBuffer);
419         break;
420     case GST_VAAPI_FILTER_OP_SHARPEN:
421         op_data->va_type = VAProcFilterSharpening;
422         op_data->va_cap_size = sizeof(VAProcFilterCap);
423         op_data->va_buffer_size = sizeof(VAProcFilterParameterBuffer);
424         break;
425     case GST_VAAPI_FILTER_OP_HUE:
426         op_data->va_subtype = VAProcColorBalanceHue;
427         goto op_colorbalance;
428     case GST_VAAPI_FILTER_OP_SATURATION:
429         op_data->va_subtype = VAProcColorBalanceSaturation;
430         goto op_colorbalance;
431     case GST_VAAPI_FILTER_OP_BRIGHTNESS:
432         op_data->va_subtype = VAProcColorBalanceBrightness;
433         goto op_colorbalance;
434     case GST_VAAPI_FILTER_OP_CONTRAST:
435         op_data->va_subtype = VAProcColorBalanceContrast;
436     op_colorbalance:
437         op_data->va_type = VAProcFilterColorBalance;
438         op_data->va_cap_size = sizeof(VAProcFilterCapColorBalance);
439         op_data->va_buffer_size = sizeof(VAProcFilterParameterBufferColorBalance);
440         break;
441     case GST_VAAPI_FILTER_OP_DEINTERLACING:
442         op_data->va_type = VAProcFilterDeinterlacing;
443         op_data->va_cap_size = sizeof(VAProcFilterCapDeinterlacing);
444         op_data->va_buffer_size = sizeof(VAProcFilterParameterBufferDeinterlacing);
445         break;
446     default:
447         g_assert(0 && "unsupported operation");
448         goto error;
449     }
450     return op_data;
451
452 error:
453     op_data_free(op_data);
454     return NULL;
455 }
456
457 static inline gpointer
458 op_data_ref(gpointer data)
459 {
460     GstVaapiFilterOpData * const op_data = data;
461
462     g_return_val_if_fail(op_data != NULL, NULL);
463
464     g_atomic_int_inc(&op_data->ref_count);
465     return op_data;
466 }
467
468 static void
469 op_data_unref(gpointer data)
470 {
471     GstVaapiFilterOpData * const op_data = data;
472
473     g_return_if_fail(op_data != NULL);
474     g_return_if_fail(op_data->ref_count > 0);
475
476     if (g_atomic_int_dec_and_test(&op_data->ref_count))
477         op_data_free(op_data);
478 }
479
480 /* Ensure capability info is set up for the VA filter we are interested in */
481 static gboolean
482 op_data_ensure_caps(GstVaapiFilterOpData *op_data, gpointer filter_caps,
483     guint num_filter_caps)
484 {
485     guchar *filter_cap = filter_caps;
486     guint i, va_num_caps = num_filter_caps;
487
488     // Find the VA filter cap matching the op info sub-type
489     if (op_data->va_subtype) {
490         for (i = 0; i < num_filter_caps; i++) {
491             /* XXX: sub-type shall always be the first field */
492             if (op_data->va_subtype == *(guint *)filter_cap) {
493                 va_num_caps= 1;
494                 break;
495             }
496             filter_cap += op_data->va_cap_size;
497         }
498         if (i == num_filter_caps)
499             return FALSE;
500     }
501
502     op_data->va_caps = g_memdup(filter_cap,
503         op_data->va_cap_size * va_num_caps);
504     if (!op_data->va_caps)
505         return FALSE;
506
507     op_data->va_num_caps = va_num_caps;
508     return TRUE;
509 }
510
511 /* Scale the filter value wrt. library spec and VA driver spec */
512 static gboolean
513 op_data_get_value_float(GstVaapiFilterOpData *op_data,
514     const VAProcFilterValueRange *range, gfloat value, gfloat *out_value_ptr)
515 {
516     GParamSpecFloat * const pspec = G_PARAM_SPEC_FLOAT(op_data->pspec);
517     gfloat out_value;
518
519     g_return_val_if_fail(range != NULL, FALSE);
520     g_return_val_if_fail(out_value_ptr != NULL, FALSE);
521
522     if (value < pspec->minimum || value > pspec->maximum)
523         return FALSE;
524
525     // Scale wrt. the medium ("default") value
526     out_value = range->default_value;
527     if (value > pspec->default_value)
528         out_value += ((value - pspec->default_value) /
529              (pspec->maximum - pspec->default_value) *
530              (range->max_value - range->default_value));
531     else if (value < pspec->default_value)
532         out_value -= ((pspec->default_value - value) /
533              (pspec->default_value - pspec->minimum) *
534              (range->default_value - range->min_value));
535
536     *out_value_ptr = out_value;
537     return TRUE;
538 }
539
540 /* Get default list of operations supported by the library */
541 static GPtrArray *
542 get_operations_default(void)
543 {
544     GPtrArray *ops;
545     guint i;
546
547     ops = g_ptr_array_new_full(N_PROPERTIES, op_data_unref);
548     if (!ops)
549         return NULL;
550
551     ensure_properties();
552
553     for (i = 0; i < N_PROPERTIES; i++) {
554         GParamSpec * const pspec = g_properties[i];
555         if (!pspec)
556             continue;
557
558         GstVaapiFilterOpData * const op_data = op_data_new(i, pspec);
559         if (!op_data)
560             goto error;
561         g_ptr_array_add(ops, op_data);
562     }
563     return ops;
564
565 error:
566     g_ptr_array_unref(ops);
567     return NULL;
568 }
569
570 /* Get the ordered list of operations, based on VA/VPP queries */
571 static GPtrArray *
572 get_operations_ordered(GstVaapiFilter *filter, GPtrArray *default_ops)
573 {
574     GPtrArray *ops;
575     VAProcFilterType *filters;
576     gpointer filter_caps = NULL;
577     guint i, j, num_filters, num_filter_caps = 0;
578
579     ops = g_ptr_array_new_full(default_ops->len, op_data_unref);
580     if (!ops)
581         return NULL;
582
583     filters = vpp_get_filters(filter, &num_filters);
584     if (!filters)
585         goto error;
586
587     // Append virtual ops first, i.e. those without an associated VA filter
588     for (i = 0; i < default_ops->len; i++) {
589         GstVaapiFilterOpData * const op_data =
590             g_ptr_array_index(default_ops, i);
591         if (op_data->va_type == VAProcFilterNone)
592             g_ptr_array_add(ops, op_data_ref(op_data));
593     }
594
595     // Append ops, while preserving the VA filters ordering
596     for (i = 0; i < num_filters; i++) {
597         const VAProcFilterType va_type = filters[i];
598         if (va_type == VAProcFilterNone)
599             continue;
600
601         for (j = 0; j < default_ops->len; j++) {
602             GstVaapiFilterOpData * const op_data =
603                 g_ptr_array_index(default_ops, j);
604             if (op_data->va_type != va_type)
605                 continue;
606
607             if (!filter_caps) {
608                 filter_caps = vpp_get_filter_caps(filter, va_type,
609                     op_data->va_cap_size, &num_filter_caps);
610                 if (!filter_caps)
611                     goto error;
612             }
613             if (!op_data_ensure_caps(op_data, filter_caps, num_filter_caps))
614                 goto error;
615             g_ptr_array_add(ops, op_data_ref(op_data));
616         }
617         free(filter_caps);
618         filter_caps = NULL;
619     }
620
621     if (filter->operations)
622         g_ptr_array_unref(filter->operations);
623     filter->operations = g_ptr_array_ref(ops);
624
625     g_free(filters);
626     g_ptr_array_unref(default_ops);
627     return ops;
628
629 error:
630     g_free(filter_caps);
631     g_free(filters);
632     g_ptr_array_unref(ops);
633     g_ptr_array_unref(default_ops);
634     return NULL;
635 }
636 #endif
637
638 /* Determine the set of supported VPP operations by the specific
639    filter, or known to this library if filter is NULL */
640 static GPtrArray *
641 get_operations(GstVaapiFilter *filter)
642 {
643 #if USE_VA_VPP
644     GPtrArray *ops;
645
646     if (filter && filter->operations)
647         return g_ptr_array_ref(filter->operations);
648
649     ops = get_operations_default();
650     if (!ops)
651         return NULL;
652     return filter ? get_operations_ordered(filter, ops) : ops;
653 #endif
654     return NULL;
655 }
656
657 /* Ensure the set of supported VPP operations is cached into the
658    GstVaapiFilter::operations member */
659 static inline gboolean
660 ensure_operations(GstVaapiFilter *filter)
661 {
662     GPtrArray *ops;
663
664     if (!filter)
665         return FALSE;
666
667     if (filter->operations)
668         return TRUE;
669
670     ops = get_operations(filter);
671     if (!ops)
672         return FALSE;
673
674     g_ptr_array_unref(ops);
675     return TRUE;
676 }
677
678 /* Find whether the VPP operation is supported or not */
679 GstVaapiFilterOpData *
680 find_operation(GstVaapiFilter *filter, GstVaapiFilterOp op)
681 {
682     guint i;
683
684     if (!ensure_operations(filter))
685         return NULL;
686
687     for (i = 0; i < filter->operations->len; i++) {
688         GstVaapiFilterOpData * const op_data =
689             g_ptr_array_index(filter->operations, i);
690         if (op_data->op == op)
691             return op_data;
692     }
693     return NULL;
694 }
695
696 /* Ensure the operation's VA buffer is allocated */
697 #if USE_VA_VPP
698 static inline gboolean
699 op_ensure_buffer(GstVaapiFilter *filter, GstVaapiFilterOpData *op_data)
700 {
701     if (G_LIKELY(op_data->va_buffer != VA_INVALID_ID))
702         return TRUE;
703     return vaapi_create_buffer(filter->va_display, filter->va_context,
704         VAProcFilterParameterBufferType, op_data->va_buffer_size, NULL,
705         &op_data->va_buffer, NULL);
706 }
707 #endif
708
709 /* Update a generic filter (float value) */
710 #if USE_VA_VPP
711 static gboolean
712 op_set_generic_unlocked(GstVaapiFilter *filter, GstVaapiFilterOpData *op_data,
713     gfloat value)
714 {
715     VAProcFilterParameterBuffer *buf;
716     VAProcFilterCap *filter_cap;
717     gfloat va_value;
718
719     if (!op_data || !op_ensure_buffer(filter, op_data))
720         return FALSE;
721
722     op_data->is_enabled =
723         (value != G_PARAM_SPEC_FLOAT(op_data->pspec)->default_value);
724     if (!op_data->is_enabled)
725         return TRUE;
726
727     filter_cap = op_data->va_caps;
728     if (!op_data_get_value_float(op_data, &filter_cap->range, value, &va_value))
729         return FALSE;
730
731     buf = vaapi_map_buffer(filter->va_display, op_data->va_buffer);
732     if (!buf)
733         return FALSE;
734
735     buf->type = op_data->va_type;
736     buf->value = va_value;
737     vaapi_unmap_buffer(filter->va_display, op_data->va_buffer, NULL);
738     return TRUE;
739 }
740 #endif
741
742 static inline gboolean
743 op_set_generic(GstVaapiFilter *filter, GstVaapiFilterOpData *op_data,
744     gfloat value)
745 {
746     gboolean success = FALSE;
747
748 #if USE_VA_VPP
749     GST_VAAPI_DISPLAY_LOCK(filter->display);
750     success = op_set_generic_unlocked(filter, op_data, value);
751     GST_VAAPI_DISPLAY_UNLOCK(filter->display);
752 #endif
753     return success;
754 }
755
756 /* Update the color balance filter */
757 #if USE_VA_VPP
758 static gboolean
759 op_set_color_balance_unlocked(GstVaapiFilter *filter,
760     GstVaapiFilterOpData *op_data, gfloat value)
761 {
762     VAProcFilterParameterBufferColorBalance *buf;
763     VAProcFilterCapColorBalance *filter_cap;
764     gfloat va_value;
765
766     if (!op_data || !op_ensure_buffer(filter, op_data))
767         return FALSE;
768
769     op_data->is_enabled =
770         (value != G_PARAM_SPEC_FLOAT(op_data->pspec)->default_value);
771     if (!op_data->is_enabled)
772         return TRUE;
773
774     filter_cap = op_data->va_caps;
775     if (!op_data_get_value_float(op_data, &filter_cap->range, value, &va_value))
776         return FALSE;
777
778     buf = vaapi_map_buffer(filter->va_display, op_data->va_buffer);
779     if (!buf)
780         return FALSE;
781
782     buf->type = op_data->va_type;
783     buf->attrib = op_data->va_subtype;
784     buf->value = va_value;
785     vaapi_unmap_buffer(filter->va_display, op_data->va_buffer, NULL);
786     return TRUE;
787 }
788 #endif
789
790 static inline gboolean
791 op_set_color_balance(GstVaapiFilter *filter, GstVaapiFilterOpData *op_data,
792     gfloat value)
793 {
794     gboolean success = FALSE;
795
796 #if USE_VA_VPP
797     GST_VAAPI_DISPLAY_LOCK(filter->display);
798     success = op_set_color_balance_unlocked(filter, op_data, value);
799     GST_VAAPI_DISPLAY_UNLOCK(filter->display);
800 #endif
801     return success;
802 }
803
804 /* Update deinterlace filter */
805 #if USE_VA_VPP
806 static gboolean
807 op_set_deinterlace_unlocked(GstVaapiFilter *filter,
808     GstVaapiFilterOpData *op_data, GstVaapiDeinterlaceMethod method,
809     guint flags)
810 {
811     VAProcFilterParameterBufferDeinterlacing *buf;
812     const VAProcFilterCapDeinterlacing *filter_caps;
813     VAProcDeinterlacingType algorithm;
814     guint i;
815
816     if (!op_data || !op_ensure_buffer(filter, op_data))
817         return FALSE;
818
819     op_data->is_enabled = (method != GST_VAAPI_DEINTERLACE_METHOD_NONE);
820     if (!op_data->is_enabled)
821         return TRUE;
822
823     algorithm = from_GstVaapiDeinterlaceMethod(method);
824     for (i = 0, filter_caps = op_data->va_caps; i < op_data->va_num_caps; i++) {
825         if (filter_caps[i].type == algorithm)
826             break;
827     }
828     if (i == op_data->va_num_caps)
829         return FALSE;
830
831     buf = vaapi_map_buffer(filter->va_display, op_data->va_buffer);
832     if (!buf)
833         return FALSE;
834
835     buf->type = op_data->va_type;
836     buf->algorithm = algorithm;
837     buf->flags = from_GstVaapiDeinterlaceFlags(flags);
838     vaapi_unmap_buffer(filter->va_display, op_data->va_buffer, NULL);
839     return TRUE;
840 }
841 #endif
842
843 static inline gboolean
844 op_set_deinterlace(GstVaapiFilter *filter, GstVaapiFilterOpData *op_data,
845     GstVaapiDeinterlaceMethod method, guint flags)
846 {
847     gboolean success = FALSE;
848
849 #if USE_VA_VPP
850     GST_VAAPI_DISPLAY_LOCK(filter->display);
851     success = op_set_deinterlace_unlocked(filter, op_data, method, flags);
852     GST_VAAPI_DISPLAY_UNLOCK(filter->display);
853 #endif
854     return success;
855 }
856
857 static gboolean
858 deint_refs_set(GArray *refs, GstVaapiSurface **surfaces, guint num_surfaces)
859 {
860     guint i;
861
862     if (num_surfaces > 0 && !surfaces)
863         return FALSE;
864
865     for (i = 0; i < num_surfaces; i++)
866         g_array_append_val(refs, GST_VAAPI_OBJECT_ID(surfaces[i]));
867     return TRUE;
868 }
869
870 static void
871 deint_refs_clear(GArray *refs)
872 {
873     if (refs->len > 0)
874         g_array_remove_range(refs, 0, refs->len);
875 }
876
877 static inline void
878 deint_refs_clear_all(GstVaapiFilter *filter)
879 {
880     deint_refs_clear(filter->forward_references);
881     deint_refs_clear(filter->backward_references);
882 }
883
884 /* ------------------------------------------------------------------------- */
885 /* --- Surface Formats                                                   --- */
886 /* ------------------------------------------------------------------------- */
887
888 static gboolean
889 ensure_formats(GstVaapiFilter *filter)
890 {
891 #if VA_CHECK_VERSION(0,34,0)
892     VASurfaceAttrib *surface_attribs = NULL;
893     guint i, num_surface_attribs = 0;
894     VAStatus va_status;
895
896     if (G_LIKELY(filter->formats))
897         return TRUE;
898
899     GST_VAAPI_DISPLAY_LOCK(filter->display);
900     va_status = vaQuerySurfaceAttributes(filter->va_display, filter->va_config,
901         NULL, &num_surface_attribs);
902     GST_VAAPI_DISPLAY_UNLOCK(filter->display);
903     if (!vaapi_check_status(va_status, "vaQuerySurfaceAttributes()"))
904         return FALSE;
905
906     surface_attribs = g_malloc(num_surface_attribs * sizeof(*surface_attribs));
907     if (!surface_attribs)
908         return FALSE;
909
910     GST_VAAPI_DISPLAY_LOCK(filter->display);
911     va_status = vaQuerySurfaceAttributes(filter->va_display, filter->va_config,
912         surface_attribs, &num_surface_attribs);
913     GST_VAAPI_DISPLAY_UNLOCK(filter->display);
914     if (!vaapi_check_status(va_status, "vaQuerySurfaceAttributes()"))
915         return FALSE;
916
917     filter->formats = g_array_sized_new(FALSE, FALSE, sizeof(GstVideoFormat),
918         num_surface_attribs);
919     if (!filter->formats)
920         goto error;
921
922     for (i = 0; i < num_surface_attribs; i++) {
923         const VASurfaceAttrib * const surface_attrib = &surface_attribs[i];
924         GstVideoFormat format;
925
926         if (surface_attrib->type != VASurfaceAttribPixelFormat)
927             continue;
928         if (!(surface_attrib->flags & VA_SURFACE_ATTRIB_SETTABLE))
929             continue;
930
931         format = gst_vaapi_video_format_from_va_fourcc(
932             surface_attrib->value.value.i);
933         if (format == GST_VIDEO_FORMAT_UNKNOWN)
934             continue;
935         g_array_append_val(filter->formats, format);
936     }
937
938     g_free(surface_attribs);
939     return TRUE;
940
941 error:
942     g_free(surface_attribs);
943 #endif
944     return FALSE;
945 }
946
947 static inline gboolean
948 is_special_format(GstVideoFormat format)
949 {
950     return format == GST_VIDEO_FORMAT_UNKNOWN ||
951         format == GST_VIDEO_FORMAT_ENCODED;
952 }
953
954 static gboolean
955 find_format(GstVaapiFilter *filter, GstVideoFormat format)
956 {
957     guint i;
958
959     if (is_special_format(format) || !filter->formats)
960         return FALSE;
961
962     for (i = 0; i < filter->formats->len; i++) {
963         if (g_array_index(filter->formats, GstVideoFormat, i) == format)
964             return TRUE;
965     }
966     return FALSE;
967 }
968
969 /* ------------------------------------------------------------------------- */
970 /* --- Interface                                                         --- */
971 /* ------------------------------------------------------------------------- */
972
973 #if USE_VA_VPP
974 static gboolean
975 gst_vaapi_filter_init(GstVaapiFilter *filter, GstVaapiDisplay *display)
976 {
977     VAStatus va_status;
978
979     filter->display     = gst_vaapi_display_ref(display);
980     filter->va_display  = GST_VAAPI_DISPLAY_VADISPLAY(display);
981     filter->va_config   = VA_INVALID_ID;
982     filter->va_context  = VA_INVALID_ID;
983     filter->format      = DEFAULT_FORMAT;
984
985     filter->forward_references =
986         g_array_sized_new(FALSE, FALSE, sizeof(VASurfaceID), 4);
987     if (!filter->forward_references)
988         return FALSE;
989
990     filter->backward_references =
991         g_array_sized_new(FALSE, FALSE, sizeof(VASurfaceID), 4);
992     if (!filter->backward_references)
993         return FALSE;
994
995     if (!GST_VAAPI_DISPLAY_HAS_VPP(display))
996         return FALSE;
997
998     va_status = vaCreateConfig(filter->va_display, VAProfileNone,
999         VAEntrypointVideoProc, NULL, 0, &filter->va_config);
1000     if (!vaapi_check_status(va_status, "vaCreateConfig() [VPP]"))
1001         return FALSE;
1002
1003     va_status = vaCreateContext(filter->va_display, filter->va_config, 0, 0, 0,
1004         NULL, 0, &filter->va_context);
1005     if (!vaapi_check_status(va_status, "vaCreateContext() [VPP]"))
1006         return FALSE;
1007     return TRUE;
1008 }
1009
1010 static void
1011 gst_vaapi_filter_finalize(GstVaapiFilter *filter)
1012 {
1013     guint i;
1014
1015     GST_VAAPI_DISPLAY_LOCK(filter->display);
1016     if (filter->operations) {
1017         for (i = 0; i < filter->operations->len; i++) {
1018             GstVaapiFilterOpData * const op_data =
1019                 g_ptr_array_index(filter->operations, i);
1020             vaapi_destroy_buffer(filter->va_display, &op_data->va_buffer);
1021         }
1022         g_ptr_array_unref(filter->operations);
1023         filter->operations = NULL;
1024     }
1025
1026     if (filter->va_context != VA_INVALID_ID) {
1027         vaDestroyContext(filter->va_display, filter->va_context);
1028         filter->va_context = VA_INVALID_ID;
1029     }
1030
1031     if (filter->va_config != VA_INVALID_ID) {
1032         vaDestroyConfig(filter->va_display, filter->va_config);
1033         filter->va_config = VA_INVALID_ID;
1034     }
1035     GST_VAAPI_DISPLAY_UNLOCK(filter->display);
1036     gst_vaapi_display_replace(&filter->display, NULL);
1037
1038     if (filter->forward_references) {
1039         g_array_unref(filter->forward_references);
1040         filter->forward_references = NULL;
1041     }
1042
1043     if (filter->backward_references) {
1044         g_array_unref(filter->backward_references);
1045         filter->backward_references = NULL;
1046     }
1047
1048     if (filter->formats) {
1049         g_array_unref(filter->formats);
1050         filter->formats = NULL;
1051     }
1052 }
1053
1054 static inline const GstVaapiMiniObjectClass *
1055 gst_vaapi_filter_class(void)
1056 {
1057     static const GstVaapiMiniObjectClass GstVaapiFilterClass = {
1058         sizeof(GstVaapiFilter),
1059         (GDestroyNotify)gst_vaapi_filter_finalize
1060     };
1061     return &GstVaapiFilterClass;
1062 }
1063 #endif
1064
1065 /**
1066  * gst_vaapi_filter_new:
1067  * @display: a #GstVaapiDisplay
1068  *
1069  * Creates a new #GstVaapiFilter set up to operate in "identity"
1070  * mode. This means that no other operation than scaling is performed.
1071  *
1072  * Return value: the newly created #GstVaapiFilter object
1073  */
1074 GstVaapiFilter *
1075 gst_vaapi_filter_new(GstVaapiDisplay *display)
1076 {
1077 #if USE_VA_VPP
1078     GstVaapiFilter *filter;
1079
1080     filter = (GstVaapiFilter *)
1081         gst_vaapi_mini_object_new0(gst_vaapi_filter_class());
1082     if (!filter)
1083         return NULL;
1084
1085     if (!gst_vaapi_filter_init(filter, display))
1086         goto error;
1087     return filter;
1088
1089 error:
1090     gst_vaapi_filter_unref(filter);
1091     return NULL;
1092 #else
1093     GST_WARNING("video processing is not supported, "
1094                 "please consider an upgrade to VA-API >= 0.34");
1095     return NULL;
1096 #endif
1097 }
1098
1099 /**
1100  * gst_vaapi_filter_ref:
1101  * @filter: a #GstVaapiFilter
1102  *
1103  * Atomically increases the reference count of the given @filter by one.
1104  *
1105  * Returns: The same @filter argument
1106  */
1107 GstVaapiFilter *
1108 gst_vaapi_filter_ref(GstVaapiFilter *filter)
1109 {
1110     g_return_val_if_fail(filter != NULL, NULL);
1111
1112     return GST_VAAPI_FILTER(gst_vaapi_mini_object_ref(
1113                                 GST_VAAPI_MINI_OBJECT(filter)));
1114 }
1115
1116 /**
1117  * gst_vaapi_filter_unref:
1118  * @filter: a #GstVaapiFilter
1119  *
1120  * Atomically decreases the reference count of the @filter by one. If
1121  * the reference count reaches zero, the filter will be free'd.
1122  */
1123 void
1124 gst_vaapi_filter_unref(GstVaapiFilter *filter)
1125 {
1126     g_return_if_fail(filter != NULL);
1127
1128     gst_vaapi_mini_object_unref(GST_VAAPI_MINI_OBJECT(filter));
1129 }
1130
1131 /**
1132  * gst_vaapi_filter_replace:
1133  * @old_filter_ptr: a pointer to a #GstVaapiFilter
1134  * @new_filter: a #GstVaapiFilter
1135  *
1136  * Atomically replaces the filter held in @old_filter_ptr with
1137  * @new_filter. This means that @old_filter_ptr shall reference a
1138  * valid filter. However, @new_filter can be NULL.
1139  */
1140 void
1141 gst_vaapi_filter_replace(GstVaapiFilter **old_filter_ptr,
1142     GstVaapiFilter *new_filter)
1143 {
1144     g_return_if_fail(old_filter_ptr != NULL);
1145
1146     gst_vaapi_mini_object_replace((GstVaapiMiniObject **)old_filter_ptr,
1147         GST_VAAPI_MINI_OBJECT(new_filter));
1148 }
1149
1150 /**
1151  * gst_vaapi_filter_get_operations:
1152  * @filter: a #GstVaapiFilter, or %NULL
1153  *
1154  * Determines the set of supported operations for video processing.
1155  * The caller owns an extra reference to the resulting array of
1156  * #GstVaapiFilterOpInfo elements, so it shall be released with
1157  * g_ptr_array_unref() after usage.
1158  *
1159  * If @filter is %NULL, then this function returns the video
1160  * processing operations supported by this library.
1161  *
1162  * Return value: the set of supported operations, or %NULL if an error
1163  *   occurred.
1164  */
1165 GPtrArray *
1166 gst_vaapi_filter_get_operations(GstVaapiFilter *filter)
1167 {
1168     return get_operations(filter);
1169 }
1170
1171 /**
1172  * gst_vaapi_filter_has_operation:
1173  * @filter: a #GstVaapiFilter
1174  * @op: a #GstVaapiFilterOp
1175  *
1176  * Determines whether the underlying VA driver advertises support for
1177  * the supplied operation @op.
1178  *
1179  * Return value: %TRUE if the specified operation may be supported by
1180  *   the underlying hardware, %FALSE otherwise
1181  */
1182 gboolean
1183 gst_vaapi_filter_has_operation(GstVaapiFilter *filter, GstVaapiFilterOp op)
1184 {
1185     g_return_val_if_fail(filter != NULL, FALSE);
1186
1187     return find_operation(filter, op) != NULL;
1188 }
1189
1190 /**
1191  * gst_vaapi_filter_use_operation:
1192  * @filter: a #GstVaapiFilter
1193  * @op: a #GstVaapiFilterOp
1194  *
1195  * Determines whether the supplied operation @op was already enabled
1196  * through a prior call to gst_vaapi_filter_set_operation() or any
1197  * other operation-specific function.
1198  *
1199  * Note: should an operation be set to its default value, this means
1200  * that it is actually not enabled.
1201  *
1202  * Return value: %TRUE if the specified operation was already enabled,
1203  *   %FALSE otherwise
1204  */
1205 gboolean
1206 gst_vaapi_filter_use_operation(GstVaapiFilter *filter, GstVaapiFilterOp op)
1207 {
1208     GstVaapiFilterOpData *op_data;
1209
1210     g_return_val_if_fail(filter != NULL, FALSE);
1211
1212     op_data = find_operation(filter, op);
1213     if (!op_data)
1214         return FALSE;
1215     return op_data->is_enabled;
1216 }
1217
1218 /**
1219  * gst_vaapi_filter_set_operation:
1220  * @filter: a #GstVaapiFilter
1221  * @op: a #GstVaapiFilterOp
1222  * @value: the @op settings
1223  *
1224  * Enable the specified operation @op to be performed during video
1225  * processing, i.e. in gst_vaapi_filter_process(). The @value argument
1226  * specifies the operation settings. e.g. deinterlacing method for
1227  * deinterlacing, denoising level for noise reduction, etc.
1228  *
1229  * If @value is %NULL, then this function resets the operation
1230  * settings to their default values.
1231  *
1232  * Return value: %TRUE if the specified operation may be supported,
1233  *   %FALSE otherwise
1234  */
1235 gboolean
1236 gst_vaapi_filter_set_operation(GstVaapiFilter *filter, GstVaapiFilterOp op,
1237     const GValue *value)
1238 {
1239 #if USE_VA_VPP
1240     GstVaapiFilterOpData *op_data;
1241
1242     g_return_val_if_fail(filter != NULL, FALSE);
1243
1244     op_data = find_operation(filter, op);
1245     if (!op_data)
1246         return FALSE;
1247
1248     if (value && !G_VALUE_HOLDS(value, G_PARAM_SPEC_VALUE_TYPE(op_data->pspec)))
1249         return FALSE;
1250
1251     switch (op) {
1252     case GST_VAAPI_FILTER_OP_FORMAT:
1253         return gst_vaapi_filter_set_format(filter, value ?
1254             g_value_get_enum(value) : DEFAULT_FORMAT);
1255     case GST_VAAPI_FILTER_OP_CROP:
1256         return gst_vaapi_filter_set_cropping_rectangle(filter, value ?
1257             g_value_get_boxed(value) : NULL);
1258     case GST_VAAPI_FILTER_OP_DENOISE:
1259     case GST_VAAPI_FILTER_OP_SHARPEN:
1260         return op_set_generic(filter, op_data,
1261             (value ? g_value_get_float(value) :
1262              G_PARAM_SPEC_FLOAT(op_data->pspec)->default_value));
1263     case GST_VAAPI_FILTER_OP_HUE:
1264     case GST_VAAPI_FILTER_OP_SATURATION:
1265     case GST_VAAPI_FILTER_OP_BRIGHTNESS:
1266     case GST_VAAPI_FILTER_OP_CONTRAST:
1267         return op_set_color_balance(filter, op_data,
1268             (value ? g_value_get_float(value) :
1269              G_PARAM_SPEC_FLOAT(op_data->pspec)->default_value));
1270     case GST_VAAPI_FILTER_OP_DEINTERLACING:
1271         return op_set_deinterlace(filter, op_data,
1272             (value ? g_value_get_enum(value) :
1273              G_PARAM_SPEC_ENUM(op_data->pspec)->default_value), 0);
1274         break;
1275     default:
1276         break;
1277     }
1278 #endif
1279     return FALSE;
1280 }
1281
1282 /**
1283  * gst_vaapi_filter_process:
1284  * @filter: a #GstVaapiFilter
1285  * @src_surface: the source @GstVaapiSurface
1286  * @dst_surface: the destination @GstVaapiSurface
1287  * @flags: #GstVaapiSurfaceRenderFlags that apply to @src_surface
1288  *
1289  * Applies the operations currently defined in the @filter to
1290  * @src_surface and return the output in @dst_surface. The order of
1291  * operations is determined in a way that suits best the underlying
1292  * hardware. i.e. the only guarantee held is the generated outcome,
1293  * not any specific order of operations.
1294  *
1295  * Return value: a #GstVaapiFilterStatus
1296  */
1297 static GstVaapiFilterStatus
1298 gst_vaapi_filter_process_unlocked(GstVaapiFilter *filter,
1299     GstVaapiSurface *src_surface, GstVaapiSurface *dst_surface, guint flags)
1300 {
1301 #if USE_VA_VPP
1302     VAProcPipelineParameterBuffer *pipeline_param = NULL;
1303     VABufferID pipeline_param_buf_id = VA_INVALID_ID;
1304     VABufferID filters[N_PROPERTIES];
1305     VAProcPipelineCaps pipeline_caps;
1306     guint i, num_filters = 0;
1307     VAStatus va_status;
1308     VARectangle src_rect, dst_rect;
1309
1310     if (!ensure_operations(filter))
1311         return GST_VAAPI_FILTER_STATUS_ERROR_ALLOCATION_FAILED;
1312
1313     /* Build surface region (source) */
1314     if (filter->use_crop_rect) {
1315         const GstVaapiRectangle * const crop_rect = &filter->crop_rect;
1316
1317         if ((crop_rect->x + crop_rect->width >
1318              GST_VAAPI_SURFACE_WIDTH(src_surface)) ||
1319             (crop_rect->y + crop_rect->height >
1320              GST_VAAPI_SURFACE_HEIGHT(src_surface)))
1321             goto error;
1322
1323         src_rect.x      = crop_rect->x;
1324         src_rect.y      = crop_rect->y;
1325         src_rect.width  = crop_rect->width;
1326         src_rect.height = crop_rect->height;
1327     }
1328     else {
1329         src_rect.x      = 0;
1330         src_rect.y      = 0;
1331         src_rect.width  = GST_VAAPI_SURFACE_WIDTH(src_surface);
1332         src_rect.height = GST_VAAPI_SURFACE_HEIGHT(src_surface);
1333     }
1334
1335     /* Build output region (target) */
1336     if (filter->use_target_rect) {
1337         const GstVaapiRectangle * const target_rect = &filter->target_rect;
1338
1339         if ((target_rect->x + target_rect->width >
1340              GST_VAAPI_SURFACE_WIDTH(dst_surface)) ||
1341             (target_rect->y + target_rect->height >
1342              GST_VAAPI_SURFACE_HEIGHT(dst_surface)))
1343             goto error;
1344
1345         dst_rect.x      = target_rect->x;
1346         dst_rect.y      = target_rect->y;
1347         dst_rect.width  = target_rect->width;
1348         dst_rect.height = target_rect->height;
1349     }
1350     else {
1351         dst_rect.x      = 0;
1352         dst_rect.y      = 0;
1353         dst_rect.width  = GST_VAAPI_SURFACE_WIDTH(dst_surface);
1354         dst_rect.height = GST_VAAPI_SURFACE_HEIGHT(dst_surface);
1355     }
1356
1357     for (i = 0, num_filters = 0; i < filter->operations->len; i++) {
1358         GstVaapiFilterOpData * const op_data =
1359             g_ptr_array_index(filter->operations, i);
1360         if (!op_data->is_enabled)
1361             continue;
1362         if (op_data->va_buffer == VA_INVALID_ID) {
1363             GST_ERROR("invalid VA buffer for operation %s",
1364                       g_param_spec_get_name(op_data->pspec));
1365             goto error;
1366         }
1367         filters[num_filters++] = op_data->va_buffer;
1368     }
1369
1370     /* Validate pipeline caps */
1371     va_status = vaQueryVideoProcPipelineCaps(filter->va_display,
1372         filter->va_context, filters, num_filters, &pipeline_caps);
1373     if (!vaapi_check_status(va_status, "vaQueryVideoProcPipelineCaps()"))
1374         goto error;
1375
1376     if (!vaapi_create_buffer(filter->va_display, filter->va_context,
1377             VAProcPipelineParameterBufferType, sizeof(*pipeline_param),
1378             NULL, &pipeline_param_buf_id, (gpointer *)&pipeline_param))
1379         goto error;
1380
1381     memset(pipeline_param, 0, sizeof(*pipeline_param));
1382     pipeline_param->surface = GST_VAAPI_OBJECT_ID(src_surface);
1383     pipeline_param->surface_region = &src_rect;
1384     pipeline_param->surface_color_standard = VAProcColorStandardNone;
1385     pipeline_param->output_region = &dst_rect;
1386     pipeline_param->output_color_standard = VAProcColorStandardNone;
1387     pipeline_param->output_background_color = 0xff000000;
1388     pipeline_param->filter_flags = from_GstVaapiSurfaceRenderFlags(flags);
1389     pipeline_param->filters = filters;
1390     pipeline_param->num_filters = num_filters;
1391
1392     // Reference frames for advanced deinterlacing
1393     if (filter->forward_references->len > 0) {
1394         pipeline_param->forward_references = (VASurfaceID *)
1395             filter->forward_references->data;
1396         pipeline_param->num_forward_references =
1397             MIN(filter->forward_references->len,
1398                 pipeline_caps.num_forward_references);
1399     }
1400     else {
1401         pipeline_param->forward_references = NULL;
1402         pipeline_param->num_forward_references = 0;
1403     }
1404
1405     if (filter->backward_references->len > 0) {
1406         pipeline_param->backward_references = (VASurfaceID *)
1407             filter->backward_references->data;
1408         pipeline_param->num_backward_references =
1409             MIN(filter->backward_references->len,
1410                 pipeline_caps.num_backward_references);
1411     }
1412     else {
1413         pipeline_param->backward_references = NULL;
1414         pipeline_param->num_backward_references = 0;
1415     }
1416
1417     vaapi_unmap_buffer(filter->va_display, pipeline_param_buf_id, NULL);
1418
1419     va_status = vaBeginPicture(filter->va_display, filter->va_context,
1420         GST_VAAPI_OBJECT_ID(dst_surface));
1421     if (!vaapi_check_status(va_status, "vaBeginPicture()"))
1422         goto error;
1423
1424     va_status = vaRenderPicture(filter->va_display, filter->va_context,
1425         &pipeline_param_buf_id, 1);
1426     if (!vaapi_check_status(va_status, "vaRenderPicture()"))
1427         goto error;
1428
1429     va_status = vaEndPicture(filter->va_display, filter->va_context);
1430     if (!vaapi_check_status(va_status, "vaEndPicture()"))
1431         goto error;
1432
1433     deint_refs_clear_all(filter);
1434     vaapi_destroy_buffer(filter->va_display, &pipeline_param_buf_id);
1435     return GST_VAAPI_FILTER_STATUS_SUCCESS;
1436
1437 error:
1438     deint_refs_clear_all(filter);
1439     vaapi_destroy_buffer(filter->va_display, &pipeline_param_buf_id);
1440     return GST_VAAPI_FILTER_STATUS_ERROR_OPERATION_FAILED;
1441 #endif
1442     return GST_VAAPI_FILTER_STATUS_ERROR_UNSUPPORTED_OPERATION;
1443 }
1444
1445 GstVaapiFilterStatus
1446 gst_vaapi_filter_process(GstVaapiFilter *filter, GstVaapiSurface *src_surface,
1447     GstVaapiSurface *dst_surface, guint flags)
1448 {
1449     GstVaapiFilterStatus status;
1450
1451     g_return_val_if_fail(filter != NULL,
1452         GST_VAAPI_FILTER_STATUS_ERROR_INVALID_PARAMETER);
1453     g_return_val_if_fail(src_surface != NULL,
1454         GST_VAAPI_FILTER_STATUS_ERROR_INVALID_PARAMETER);
1455     g_return_val_if_fail(dst_surface != NULL,
1456         GST_VAAPI_FILTER_STATUS_ERROR_INVALID_PARAMETER);
1457
1458     GST_VAAPI_DISPLAY_LOCK(filter->display);
1459     status = gst_vaapi_filter_process_unlocked(filter,
1460         src_surface, dst_surface, flags);
1461     GST_VAAPI_DISPLAY_UNLOCK(filter->display);
1462     return status;
1463 }
1464
1465 /**
1466  * gst_vaapi_filter_get_formats:
1467  * @filter: a #GstVaapiFilter
1468  *
1469  * Determines the set of supported source or target formats for video
1470  * processing.  The caller owns an extra reference to the resulting
1471  * array of #GstVideoFormat elements, so it shall be released with
1472  * g_array_unref() after usage.
1473  *
1474  * Return value: the set of supported target formats for video processing.
1475  */
1476 GArray *
1477 gst_vaapi_filter_get_formats(GstVaapiFilter *filter)
1478 {
1479     g_return_val_if_fail(filter != NULL, NULL);
1480
1481     if (!ensure_formats(filter))
1482         return NULL;
1483     return g_array_ref(filter->formats);
1484 }
1485
1486 /**
1487  * gst_vaapi_filter_set_format:
1488  * @filter: a #GstVaapiFilter
1489  * @format: the target surface format
1490  *
1491  * Sets the desired pixel format of the resulting video processing
1492  * operations.
1493  *
1494  * If @format is #GST_VIDEO_FORMAT_UNKNOWN, the filter will assume iso
1495  * format conversion, i.e. no color conversion at all and the target
1496  * surface format shall match the source surface format.
1497  *
1498  * If @format is #GST_VIDEO_FORMAT_ENCODED, the filter will use the pixel
1499  * format of the target surface passed to gst_vaapi_filter_process().
1500  *
1501  * Return value: %TRUE if the color conversion to the specified @format
1502  *   may be supported, %FALSE otherwise.
1503  */
1504 gboolean
1505 gst_vaapi_filter_set_format(GstVaapiFilter *filter, GstVideoFormat format)
1506 {
1507     g_return_val_if_fail(filter != NULL, FALSE);
1508
1509     if (!ensure_formats(filter))
1510         return FALSE;
1511
1512     if (!is_special_format(format) && !find_format(filter, format))
1513         return FALSE;
1514
1515     filter->format = format;
1516     return TRUE;
1517 }
1518
1519 /**
1520  * gst_vaapi_filter_set_cropping_rectangle:
1521  * @filter: a #GstVaapiFilter
1522  * @rect: the cropping region
1523  *
1524  * Sets the source surface cropping rectangle to use during the video
1525  * processing. If @rect is %NULL, the whole source surface will be used.
1526  *
1527  * Return value: %TRUE if the operation is supported, %FALSE otherwise.
1528  */
1529 gboolean
1530 gst_vaapi_filter_set_cropping_rectangle(GstVaapiFilter *filter,
1531     const GstVaapiRectangle *rect)
1532 {
1533     g_return_val_if_fail(filter != NULL, FALSE);
1534
1535     filter->use_crop_rect = rect != NULL;
1536     if (filter->use_crop_rect)
1537         filter->crop_rect = *rect;
1538     return TRUE;
1539 }
1540
1541 /**
1542  * gst_vaapi_filter_set_target_rectangle:
1543  * @filter: a #GstVaapiFilter
1544  * @rect: the target render region
1545  *
1546  * Sets the region within the target surface where the source surface
1547  * would be rendered. i.e. where the hardware accelerator would emit
1548  * the outcome of video processing. If @rect is %NULL, the whole
1549  * source surface will be used.
1550  *
1551  * Return value: %TRUE if the operation is supported, %FALSE otherwise.
1552  */
1553 gboolean
1554 gst_vaapi_filter_set_target_rectangle(GstVaapiFilter *filter,
1555     const GstVaapiRectangle *rect)
1556 {
1557     g_return_val_if_fail(filter != NULL, FALSE);
1558
1559     filter->use_target_rect = rect != NULL;
1560     if (filter->use_target_rect)
1561         filter->target_rect = *rect;
1562     return TRUE;
1563 }
1564
1565 /**
1566  * gst_vaapi_filter_set_denoising_level:
1567  * @filter: a #GstVaapiFilter
1568  * @level: the level of noise reduction to apply
1569  *
1570  * Sets the noise reduction level to apply. If @level is 0.0f, this
1571  * corresponds to disabling the noise reduction algorithm.
1572  *
1573  * Return value: %TRUE if the operation is supported, %FALSE otherwise.
1574  */
1575 gboolean
1576 gst_vaapi_filter_set_denoising_level(GstVaapiFilter *filter, gfloat level)
1577 {
1578     g_return_val_if_fail(filter != NULL, FALSE);
1579
1580     return op_set_generic(filter,
1581         find_operation(filter, GST_VAAPI_FILTER_OP_DENOISE), level);
1582 }
1583
1584 /**
1585  * gst_vaapi_filter_set_sharpening_level:
1586  * @filter: a #GstVaapiFilter
1587  * @level: the sharpening factor
1588  *
1589  * Enables noise reduction with the specified factor.
1590  *
1591  * Return value: %TRUE if the operation is supported, %FALSE otherwise.
1592  */
1593 gboolean
1594 gst_vaapi_filter_set_sharpening_level(GstVaapiFilter *filter, gfloat level)
1595 {
1596     g_return_val_if_fail(filter != NULL, FALSE);
1597
1598     return op_set_generic(filter,
1599         find_operation(filter, GST_VAAPI_FILTER_OP_SHARPEN), level);
1600 }
1601
1602 /**
1603  * gst_vaapi_filter_set_hue:
1604  * @filter: a #GstVaapiFilter
1605  * @value: the color hue value
1606  *
1607  * Enables color hue adjustment to the specified value.
1608  *
1609  * Return value: %TRUE if the operation is supported, %FALSE otherwise.
1610  */
1611 gboolean
1612 gst_vaapi_filter_set_hue(GstVaapiFilter *filter, gfloat value)
1613 {
1614     g_return_val_if_fail(filter != NULL, FALSE);
1615
1616     return op_set_color_balance(filter,
1617         find_operation(filter, GST_VAAPI_FILTER_OP_HUE), value);
1618 }
1619
1620 /**
1621  * gst_vaapi_filter_set_saturation:
1622  * @filter: a #GstVaapiFilter
1623  * @value: the color saturation value
1624  *
1625  * Enables color saturation adjustment to the specified value.
1626  *
1627  * Return value: %TRUE if the operation is supported, %FALSE otherwise.
1628  */
1629 gboolean
1630 gst_vaapi_filter_set_saturation(GstVaapiFilter *filter, gfloat value)
1631 {
1632     g_return_val_if_fail(filter != NULL, FALSE);
1633
1634     return op_set_color_balance(filter,
1635         find_operation(filter, GST_VAAPI_FILTER_OP_SATURATION), value);
1636 }
1637
1638 /**
1639  * gst_vaapi_filter_set_brightness:
1640  * @filter: a #GstVaapiFilter
1641  * @value: the color brightness value
1642  *
1643  * Enables color brightness adjustment to the specified value.
1644  *
1645  * Return value: %TRUE if the operation is supported, %FALSE otherwise.
1646  */
1647 gboolean
1648 gst_vaapi_filter_set_brightness(GstVaapiFilter *filter, gfloat value)
1649 {
1650     g_return_val_if_fail(filter != NULL, FALSE);
1651
1652     return op_set_color_balance(filter,
1653         find_operation(filter, GST_VAAPI_FILTER_OP_BRIGHTNESS), value);
1654 }
1655
1656 /**
1657  * gst_vaapi_filter_set_contrast:
1658  * @filter: a #GstVaapiFilter
1659  * @value: the color contrast value
1660  *
1661  * Enables color contrast adjustment to the specified value.
1662  *
1663  * Return value: %TRUE if the operation is supported, %FALSE otherwise.
1664  */
1665 gboolean
1666 gst_vaapi_filter_set_contrast(GstVaapiFilter *filter, gfloat value)
1667 {
1668     g_return_val_if_fail(filter != NULL, FALSE);
1669
1670     return op_set_color_balance(filter,
1671         find_operation(filter, GST_VAAPI_FILTER_OP_CONTRAST), value);
1672 }
1673
1674 /**
1675  * gst_vaapi_filter_set_deinterlacing:
1676  * @filter: a #GstVaapiFilter
1677  * @method: the deinterlacing algorithm (see #GstVaapiDeinterlaceMethod)
1678  * @flags: the additional flags
1679  *
1680  * Applies deinterlacing to the video processing pipeline. If @method
1681  * is not @GST_VAAPI_DEINTERLACE_METHOD_NONE, then @flags could
1682  * represent the initial picture structure of the source frame.
1683  *
1684  * Return value: %TRUE if the operation is supported, %FALSE otherwise.
1685  */
1686 gboolean
1687 gst_vaapi_filter_set_deinterlacing(GstVaapiFilter *filter,
1688     GstVaapiDeinterlaceMethod method, guint flags)
1689 {
1690     g_return_val_if_fail(filter != NULL, FALSE);
1691
1692     return op_set_deinterlace(filter,
1693         find_operation(filter, GST_VAAPI_FILTER_OP_DEINTERLACING), method,
1694         flags);
1695 }
1696
1697 /**
1698  * gst_vaapi_filter_set_deinterlacing_references:
1699  * @filter: a #GstVaapiFilter
1700  * @forward_references: the set of #GstVaapiSurface objects used as
1701  *   forward references
1702  * @num_forward_references: the number of elements in the
1703  *   @forward_references array
1704  * @backward_references: the set of #GstVaapiSurface objects used as
1705  *   backward references
1706  * @num_backward_references: the number of elements in the
1707  *   @backward_references array
1708  *
1709  * Specifies the list of surfaces used for forward or backward reference in
1710  * advanced deinterlacing mode. The caller is responsible for maintaining
1711  * the associated surfaces live until gst_vaapi_filter_process() completes.
1712  * e.g. by holding an extra reference to the associated #GstVaapiSurfaceProxy.
1713  *
1714  * Temporal ordering is maintained as follows: the shorter index in
1715  * either array is, the closest the matching surface is relatively to
1716  * the current source surface to process. e.g. surface in
1717  * @forward_references array index 0 represents the immediately
1718  * preceding surface in display order, surface at index 1 is the one
1719  * preceding surface at index 0, etc.
1720  *
1721  * The video processing filter will only use the recommended number of
1722  * surfaces for backward and forward references.
1723  *
1724  * Note: the supplied lists of reference surfaces are not sticky. This
1725  * means that they are only valid for the next gst_vaapi_filter_process()
1726  * call, and thus needs to be submitted again for subsequent calls.
1727  *
1728  * Return value: %TRUE if the operation is supported, %FALSE otherwise.
1729  */
1730 gboolean
1731 gst_vaapi_filter_set_deinterlacing_references(GstVaapiFilter *filter,
1732     GstVaapiSurface **forward_references, guint num_forward_references,
1733     GstVaapiSurface **backward_references, guint num_backward_references)
1734 {
1735     g_return_val_if_fail(filter != NULL, FALSE);
1736
1737     deint_refs_clear_all(filter);
1738
1739     if (!deint_refs_set(filter->forward_references, forward_references,
1740             num_forward_references))
1741         return FALSE;
1742
1743     if (!deint_refs_set(filter->backward_references, backward_references,
1744             num_backward_references))
1745         return FALSE;
1746     return TRUE;
1747 }