vafilter: Increase the caps for HDR.
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / sys / va / gstvafilter.c
1 /* GStreamer
2  * Copyright (C) 2020 Igalia, S.L.
3  *     Author: Víctor Jáquez <vjaquez@igalia.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "gstvafilter.h"
26
27 #include <gst/va/gstvavideoformat.h>
28 #include <gst/va/vasurfaceimage.h>
29 #include <gst/video/video.h>
30 #include <va/va_drmcommon.h>
31
32 #include "gstvacaps.h"
33 #include "gstvadisplay_priv.h"
34
35 struct _GstVaFilter
36 {
37   GstObject parent;
38
39   GstVaDisplay *display;
40   VAConfigID config;
41   VAContextID context;
42
43   /* hardware constraints */
44   VAProcPipelineCaps pipeline_caps;
45
46   guint32 mem_types;
47   gint min_width;
48   gint max_width;
49   gint min_height;
50   gint max_height;
51
52   GArray *surface_formats;
53   GArray *image_formats;
54
55   GArray *available_filters;
56
57   /* stream information */
58   guint mirror;
59   guint rotation;
60   GstVideoOrientationMethod orientation;
61
62   guint32 scale_method;
63
64   gboolean crop_enabled;
65
66   VARectangle input_region;
67   VARectangle output_region;
68
69   VAProcColorStandardType input_color_standard;
70   VAProcColorProperties input_color_properties;
71   VAProcColorStandardType output_color_standard;
72   VAProcColorProperties output_color_properties;
73
74   GArray *filters;
75 };
76
77 GST_DEBUG_CATEGORY_STATIC (gst_va_filter_debug);
78 #define GST_CAT_DEFAULT gst_va_filter_debug
79
80 #define gst_va_filter_parent_class parent_class
81 G_DEFINE_TYPE_WITH_CODE (GstVaFilter, gst_va_filter, GST_TYPE_OBJECT,
82     GST_DEBUG_CATEGORY_INIT (gst_va_filter_debug, "vafilter", 0, "VA Filter"));
83
84 enum
85 {
86   PROP_DISPLAY = 1,
87   N_PROPERTIES
88 };
89
90 static GParamSpec *g_properties[N_PROPERTIES];
91
92 static void
93 gst_va_filter_set_property (GObject * object, guint prop_id,
94     const GValue * value, GParamSpec * pspec)
95 {
96   GstVaFilter *self = GST_VA_FILTER (object);
97
98   switch (prop_id) {
99     case PROP_DISPLAY:{
100       g_assert (!self->display);
101       self->display = g_value_dup_object (value);
102       break;
103     }
104     default:
105       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
106       break;
107   }
108 }
109
110 static void
111 gst_va_filter_get_property (GObject * object, guint prop_id, GValue * value,
112     GParamSpec * pspec)
113 {
114   GstVaFilter *self = GST_VA_FILTER (object);
115
116   switch (prop_id) {
117     case PROP_DISPLAY:
118       g_value_set_object (value, self->display);
119       break;
120     default:
121       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
122       break;
123   }
124 }
125
126 static void
127 gst_va_filter_dispose (GObject * object)
128 {
129   GstVaFilter *self = GST_VA_FILTER (object);
130
131   gst_va_filter_close (self);
132
133   g_clear_pointer (&self->available_filters, g_array_unref);
134   g_clear_pointer (&self->image_formats, g_array_unref);
135   g_clear_pointer (&self->surface_formats, g_array_unref);
136   gst_clear_object (&self->display);
137
138   G_OBJECT_CLASS (parent_class)->dispose (object);
139 }
140
141 static void
142 gst_va_filter_class_init (GstVaFilterClass * klass)
143 {
144   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
145
146   gobject_class->set_property = gst_va_filter_set_property;
147   gobject_class->get_property = gst_va_filter_get_property;
148   gobject_class->dispose = gst_va_filter_dispose;
149
150   g_properties[PROP_DISPLAY] =
151       g_param_spec_object ("display", "GstVaDisplay", "GstVADisplay object",
152       GST_TYPE_VA_DISPLAY,
153       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
154
155   g_object_class_install_properties (gobject_class, N_PROPERTIES, g_properties);
156 }
157
158 static void
159 gst_va_filter_init (GstVaFilter * self)
160 {
161   self->config = VA_INVALID_ID;
162   self->context = VA_INVALID_ID;
163
164   self->min_height = 1;
165   self->max_height = G_MAXINT;
166   self->min_width = 1;
167   self->max_width = G_MAXINT;
168 }
169
170 GstVaFilter *
171 gst_va_filter_new (GstVaDisplay * display)
172 {
173   g_return_val_if_fail (GST_IS_VA_DISPLAY (display), NULL);
174
175   return g_object_new (GST_TYPE_VA_FILTER, "display", display, NULL);
176 }
177
178 gboolean
179 gst_va_filter_is_open (GstVaFilter * self)
180 {
181   gboolean ret;
182
183   g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
184
185   GST_OBJECT_LOCK (self);
186   ret = (self->config != VA_INVALID_ID && self->context != VA_INVALID_ID);
187   GST_OBJECT_UNLOCK (self);
188   return ret;
189 }
190
191 static gboolean
192 gst_va_filter_ensure_config_attributes (GstVaFilter * self,
193     guint32 * rt_formats_ptr)
194 {
195   VAConfigAttrib attribs[] = {
196     {.type = VAConfigAttribMaxPictureWidth,},
197     {.type = VAConfigAttribMaxPictureHeight,},
198     {.type = VAConfigAttribRTFormat,},
199   };
200   VADisplay dpy;
201   VAStatus status;
202   guint i, value, rt_formats = 0, max_width = 0, max_height = 0;
203
204   dpy = gst_va_display_get_va_dpy (self->display);
205
206   status = vaGetConfigAttributes (dpy, VAProfileNone, VAEntrypointVideoProc,
207       attribs, G_N_ELEMENTS (attribs));
208   if (status != VA_STATUS_SUCCESS) {
209     GST_ERROR_OBJECT (self, "vaGetConfigAttributes: %s", vaErrorStr (status));
210     return FALSE;
211   }
212
213   for (i = 0; i < G_N_ELEMENTS (attribs); i++) {
214     value = attribs[i].value;
215     if (value == VA_ATTRIB_NOT_SUPPORTED)
216       continue;
217     switch (attribs[i].type) {
218       case VAConfigAttribMaxPictureHeight:
219         max_height = value;
220         break;
221       case VAConfigAttribMaxPictureWidth:
222         max_width = value;
223         break;
224       case VAConfigAttribRTFormat:
225         rt_formats = value;
226         break;
227       default:
228         break;
229     }
230   }
231
232   if (rt_formats_ptr && rt_formats != 0)
233     *rt_formats_ptr = rt_formats;
234   if (max_width > 0 && max_width < G_MAXINT)
235     self->max_width = max_width;
236   if (max_height > 0 && max_height < G_MAXINT)
237     self->max_height = max_height;
238
239   return TRUE;
240 }
241
242 /* There are formats that are not handled correctly by driver */
243 static gboolean
244 format_is_accepted (GstVaFilter * self, GstVideoFormat format)
245 {
246   /* https://github.com/intel/media-driver/issues/690
247    * https://github.com/intel/media-driver/issues/644 */
248   if (!GST_VA_DISPLAY_IS_IMPLEMENTATION (self->display, INTEL_IHD))
249     return TRUE;
250
251   switch (format) {
252     case GST_VIDEO_FORMAT_ARGB:
253     case GST_VIDEO_FORMAT_xRGB:
254     case GST_VIDEO_FORMAT_ABGR:
255     case GST_VIDEO_FORMAT_xBGR:
256       return FALSE;
257     default:
258       break;
259   }
260
261   return TRUE;
262 }
263
264 static gboolean
265 gst_va_filter_ensure_surface_attributes (GstVaFilter * self)
266 {
267   GArray *surface_formats;
268   GstVideoFormat format;
269   VASurfaceAttrib *attribs;
270   guint i, attrib_count;
271
272   attribs =
273       gst_va_get_surface_attribs (self->display, self->config, &attrib_count);
274   if (!attribs)
275     return FALSE;
276   surface_formats = g_array_new (FALSE, FALSE, sizeof (GstVideoFormat));
277
278   for (i = 0; i < attrib_count; i++) {
279     if (attribs[i].value.type != VAGenericValueTypeInteger)
280       continue;
281     switch (attribs[i].type) {
282       case VASurfaceAttribPixelFormat:
283         format = gst_va_video_format_from_va_fourcc (attribs[i].value.value.i);
284         if (format != GST_VIDEO_FORMAT_UNKNOWN
285             && format_is_accepted (self, format))
286           g_array_append_val (surface_formats, format);
287         break;
288       case VASurfaceAttribMinWidth:
289         self->min_width = MAX (self->min_width, attribs[i].value.value.i);
290         break;
291       case VASurfaceAttribMaxWidth:
292         if (self->max_width > 0)
293           self->max_width = MIN (self->max_width, attribs[i].value.value.i);
294         else
295           self->max_width = attribs[i].value.value.i;
296         break;
297       case VASurfaceAttribMinHeight:
298         self->min_height = MAX (self->min_height, attribs[i].value.value.i);
299         break;
300       case VASurfaceAttribMaxHeight:
301         if (self->max_height > 0)
302           self->max_height = MIN (self->max_height, attribs[i].value.value.i);
303         else
304           self->max_height = attribs[i].value.value.i;
305         break;
306       case VASurfaceAttribMemoryType:
307         self->mem_types = attribs[i].value.value.i;
308         break;
309       default:
310         break;
311     }
312   }
313
314   if (surface_formats->len == 0)
315     g_clear_pointer (&surface_formats, g_array_unref);
316
317   self->surface_formats = surface_formats;
318
319   g_free (attribs);
320
321   return TRUE;
322 }
323
324 static gboolean
325 gst_va_filter_ensure_pipeline_caps (GstVaFilter * self)
326 {
327   VADisplay dpy;
328   VAStatus status;
329
330   dpy = gst_va_display_get_va_dpy (self->display);
331
332   status = vaQueryVideoProcPipelineCaps (dpy, self->context, NULL, 0,
333       &self->pipeline_caps);
334   if (status != VA_STATUS_SUCCESS) {
335     GST_ERROR_OBJECT (self, "vaQueryVideoProcPipelineCaps: %s",
336         vaErrorStr (status));
337     return FALSE;
338   }
339
340   return TRUE;
341 }
342
343 /* Not thread-safe API */
344 gboolean
345 gst_va_filter_open (GstVaFilter * self)
346 {
347   VAConfigAttrib attrib = {
348     .type = VAConfigAttribRTFormat,
349   };
350   VADisplay dpy;
351   VAStatus status;
352
353   g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
354
355   if (gst_va_filter_is_open (self))
356     return TRUE;
357
358   if (!gst_va_filter_ensure_config_attributes (self, &attrib.value))
359     return FALSE;
360
361   if (!gst_va_filter_ensure_pipeline_caps (self))
362     return FALSE;
363
364   self->image_formats = gst_va_display_get_image_formats (self->display);
365   if (!self->image_formats)
366     return FALSE;
367
368   dpy = gst_va_display_get_va_dpy (self->display);
369
370   status = vaCreateConfig (dpy, VAProfileNone, VAEntrypointVideoProc, &attrib,
371       1, &self->config);
372   if (status != VA_STATUS_SUCCESS) {
373     GST_ERROR_OBJECT (self, "vaCreateConfig: %s", vaErrorStr (status));
374     return FALSE;
375   }
376
377   if (!gst_va_filter_ensure_surface_attributes (self))
378     goto bail;
379
380   status = vaCreateContext (dpy, self->config, 0, 0, 0, NULL, 0,
381       &self->context);
382   if (status != VA_STATUS_SUCCESS) {
383     GST_ERROR_OBJECT (self, "vaCreateContext: %s", vaErrorStr (status));
384     goto bail;
385   }
386
387   return TRUE;
388
389 bail:
390   {
391     status = vaDestroyConfig (dpy, self->config);
392
393     return FALSE;
394   }
395 }
396
397 /* Not thread-safe API */
398 gboolean
399 gst_va_filter_close (GstVaFilter * self)
400 {
401   VADisplay dpy;
402   VAStatus status;
403
404   g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
405
406   if (!gst_va_filter_is_open (self))
407     return TRUE;
408
409   dpy = gst_va_display_get_va_dpy (self->display);
410
411   if (self->context != VA_INVALID_ID) {
412     status = vaDestroyContext (dpy, self->context);
413     if (status != VA_STATUS_SUCCESS)
414       GST_ERROR_OBJECT (self, "vaDestroyContext: %s", vaErrorStr (status));
415   }
416
417   status = vaDestroyConfig (dpy, self->config);
418   if (status != VA_STATUS_SUCCESS) {
419     GST_ERROR_OBJECT (self, "vaDestroyConfig: %s", vaErrorStr (status));
420     return FALSE;
421   }
422
423   g_clear_pointer (&self->available_filters, g_array_unref);
424   g_clear_pointer (&self->filters, g_array_unref);
425
426   gst_va_filter_init (self);
427
428   return TRUE;
429 }
430
431 /* *INDENT-OFF* */
432 static const struct VaFilterCapMap {
433   VAProcFilterType type;
434   guint count;
435   const char *name;
436 } filter_cap_map[] = {
437 #define F(name, count) { G_PASTE (VAProcFilter, name), count, G_STRINGIFY (name) }
438   F(NoiseReduction, 1),
439   F(Deinterlacing, VAProcDeinterlacingCount),
440   F(Sharpening, 1),
441   F(ColorBalance, VAProcColorBalanceCount),
442   F(SkinToneEnhancement, 1),
443   F(TotalColorCorrection, VAProcTotalColorCorrectionCount),
444   F(HVSNoiseReduction, 0),
445   F(HighDynamicRangeToneMapping, VAProcHighDynamicRangeMetadataTypeCount),
446 #if VA_CHECK_VERSION (1, 12, 0)
447   F(3DLUT, 16),
448 #endif
449 #undef F
450 };
451 /* *INDENT-ON* */
452
453 static const struct VaFilterCapMap *
454 gst_va_filter_get_filter_cap (VAProcFilterType type)
455 {
456   guint i;
457
458   for (i = 0; i < G_N_ELEMENTS (filter_cap_map); i++) {
459     if (filter_cap_map[i].type == type)
460       return &filter_cap_map[i];
461   }
462
463   return NULL;
464 }
465
466 static guint
467 gst_va_filter_get_filter_cap_count (VAProcFilterType type)
468 {
469   const struct VaFilterCapMap *map = gst_va_filter_get_filter_cap (type);
470   return map ? map->count : 0;
471 }
472
473 struct VaFilter
474 {
475   VAProcFilterType type;
476   guint num_caps;
477   union
478   {
479     VAProcFilterCap simple;
480     VAProcFilterCapDeinterlacing deint[VAProcDeinterlacingCount];
481     VAProcFilterCapColorBalance cb[VAProcColorBalanceCount];
482     VAProcFilterCapTotalColorCorrection cc[VAProcTotalColorCorrectionCount];
483       VAProcFilterCapHighDynamicRange
484         hdr[VAProcHighDynamicRangeMetadataTypeCount];
485 #if VA_CHECK_VERSION (1, 12, 0)
486     VAProcFilterCap3DLUT lut[16];
487 #endif
488   } caps;
489 };
490
491 static gboolean
492 gst_va_filter_ensure_filters (GstVaFilter * self)
493 {
494   GArray *filters;
495   VADisplay dpy;
496   VAProcFilterType *filter_types;
497   VAStatus status;
498   guint i, num = VAProcFilterCount;
499   gboolean ret = FALSE;
500
501   GST_OBJECT_LOCK (self);
502   if (self->available_filters) {
503     GST_OBJECT_UNLOCK (self);
504     return TRUE;
505   }
506   GST_OBJECT_UNLOCK (self);
507
508   filter_types = g_malloc_n (num, sizeof (*filter_types));
509
510   dpy = gst_va_display_get_va_dpy (self->display);
511
512   status = vaQueryVideoProcFilters (dpy, self->context, filter_types, &num);
513   if (status == VA_STATUS_ERROR_MAX_NUM_EXCEEDED) {
514     filter_types = g_try_realloc_n (filter_types, num, sizeof (*filter_types));
515     status = vaQueryVideoProcFilters (dpy, self->context, filter_types, &num);
516   }
517   if (status != VA_STATUS_SUCCESS) {
518     GST_ERROR_OBJECT (self, "vaQueryVideoProcFilters: %s", vaErrorStr (status));
519     goto bail;
520   }
521
522   if (num == 0)
523     goto bail;
524
525   filters = g_array_sized_new (FALSE, FALSE, sizeof (struct VaFilter), num);
526
527   for (i = 0; i < num; i++) {
528     guint num_caps = gst_va_filter_get_filter_cap_count (filter_types[i]);
529     struct VaFilter filter = { filter_types[i], num_caps, {{{0,}}} };
530
531     if (num_caps > 0) {
532       status = vaQueryVideoProcFilterCaps (dpy, self->context, filter.type,
533           &filter.caps, &filter.num_caps);
534       if (status != VA_STATUS_SUCCESS) {
535         GST_WARNING_OBJECT (self, "vaQueryVideoProcFiltersCaps: %s",
536             vaErrorStr (status));
537         continue;
538       }
539     }
540
541     g_array_append_val (filters, filter);
542   }
543
544   GST_OBJECT_LOCK (self);
545   g_clear_pointer (&self->available_filters, g_array_unref);
546   self->available_filters = filters;
547   GST_OBJECT_UNLOCK (self);
548
549   ret = TRUE;
550
551 bail:
552   g_free (filter_types);
553
554   return ret;
555 }
556
557 /* *INDENT-OFF* */
558 static const struct _CBDesc {
559   const char *name;
560   const char *nick;
561   const char *blurb;
562   guint prop_id;
563 } cb_desc[VAProcColorBalanceCount] = {
564   [VAProcColorBalanceHue] =
565       { "hue", "Hue", "Color hue value", GST_VA_FILTER_PROP_HUE },
566   [VAProcColorBalanceSaturation] =
567       { "saturation", "Saturation", "Color saturation value",
568         GST_VA_FILTER_PROP_SATURATION },
569   [VAProcColorBalanceBrightness] =
570       { "brightness", "Brightness", "Color brightness value",
571         GST_VA_FILTER_PROP_BRIGHTNESS },
572   [VAProcColorBalanceContrast] =
573       { "contrast", "Contrast", "Color contrast value",
574         GST_VA_FILTER_PROP_CONTRAST },
575   [VAProcColorBalanceAutoSaturation] =
576       { "auto-saturation",   "Auto-Saturation", "Enable auto saturation",
577         GST_VA_FILTER_PROP_AUTO_SATURATION },
578   [VAProcColorBalanceAutoBrightness] =
579       { "auto-brightness", "Auto-Brightness", "Enable auto brightness",
580         GST_VA_FILTER_PROP_AUTO_BRIGHTNESS    },
581   [VAProcColorBalanceAutoContrast] =
582       { "auto-contrast", "Auto-Contrast", "Enable auto contrast",
583         GST_VA_FILTER_PROP_AUTO_CONTRAST },
584 };
585 /* *INDENT-ON* */
586
587 gboolean
588 gst_va_filter_install_properties (GstVaFilter * self, GObjectClass * klass)
589 {
590   guint i;
591   const GParamFlags common_flags = G_PARAM_READWRITE
592       | GST_PARAM_CONDITIONALLY_AVAILABLE | G_PARAM_STATIC_STRINGS
593       | GST_PARAM_MUTABLE_PLAYING | GST_PARAM_CONTROLLABLE;
594
595   g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
596
597   if (!gst_va_filter_is_open (self))
598     return FALSE;
599
600   if (!gst_va_filter_ensure_filters (self))
601     return FALSE;
602
603   for (i = 0; i < self->available_filters->len; i++) {
604     const struct VaFilter *filter =
605         &g_array_index (self->available_filters, struct VaFilter, i);
606
607     switch (filter->type) {
608       case VAProcFilterNoiseReduction:{
609         const VAProcFilterCap *caps = &filter->caps.simple;
610
611         g_object_class_install_property (klass, GST_VA_FILTER_PROP_DENOISE,
612             g_param_spec_float ("denoise", "Noise reduction",
613                 "Noise reduction factor", caps->range.min_value,
614                 caps->range.max_value, caps->range.default_value,
615                 common_flags));
616         break;
617       }
618       case VAProcFilterSharpening:{
619         const VAProcFilterCap *caps = &filter->caps.simple;
620
621         g_object_class_install_property (klass, GST_VA_FILTER_PROP_SHARPEN,
622             g_param_spec_float ("sharpen", "Sharpening Level",
623                 "Sharpening/blurring filter", caps->range.min_value,
624                 caps->range.max_value, caps->range.default_value,
625                 common_flags));
626         break;
627       }
628       case VAProcFilterSkinToneEnhancement:{
629         const VAProcFilterCap *caps = &filter->caps.simple;
630         GParamSpec *pspec;
631
632         /* i965 filter */
633         if (filter->num_caps == 0) {
634           pspec = g_param_spec_boolean ("skin-tone", "Skin Tone Enhancenment",
635               "Skin Tone Enhancenment filter", FALSE, common_flags);
636         } else {
637           pspec = g_param_spec_float ("skin-tone", "Skin Tone Enhancenment",
638               "Skin Tone Enhancenment filter", caps->range.min_value,
639               caps->range.max_value, caps->range.default_value, common_flags);
640         }
641
642         g_object_class_install_property (klass, GST_VA_FILTER_PROP_SKINTONE,
643             pspec);
644         break;
645       }
646       case VAProcFilterColorBalance:{
647         const VAProcFilterCapColorBalance *caps = filter->caps.cb;
648         GParamSpec *pspec;
649         guint j, k;
650
651         for (j = 0; j < filter->num_caps; j++) {
652           k = caps[j].type;
653           if (caps[j].range.min_value < caps[j].range.max_value) {
654             pspec = g_param_spec_float (cb_desc[k].name, cb_desc[k].nick,
655                 cb_desc[k].blurb, caps[j].range.min_value,
656                 caps[j].range.max_value, caps[j].range.default_value,
657                 common_flags);
658           } else {
659             pspec = g_param_spec_boolean (cb_desc[k].name, cb_desc[k].nick,
660                 cb_desc[k].blurb, FALSE, common_flags);
661           }
662
663           g_object_class_install_property (klass, cb_desc[k].prop_id, pspec);
664         }
665
666         break;
667       }
668       case VAProcFilterHighDynamicRangeToneMapping:{
669         guint j;
670         for (j = 0; j < filter->num_caps; j++) {
671           const VAProcFilterCapHighDynamicRange *caps = &filter->caps.hdr[j];
672           if (caps->metadata_type == VAProcHighDynamicRangeMetadataHDR10
673               && (caps->caps_flag & VA_TONE_MAPPING_HDR_TO_SDR)) {
674             g_object_class_install_property (klass, GST_VA_FILTER_PROP_HDR,
675                 g_param_spec_boolean ("hdr-tone-mapping", "HDR tone mapping",
676                     "Enable HDR to SDR tone mapping", FALSE, common_flags));
677             break;
678           }
679         }
680       }
681       default:
682         break;
683     }
684   }
685
686   if (self->pipeline_caps.mirror_flags != VA_MIRROR_NONE
687       || self->pipeline_caps.rotation_flags != VA_ROTATION_NONE) {
688     g_object_class_install_property (klass, GST_VA_FILTER_PROP_VIDEO_DIR,
689         g_param_spec_enum ("video-direction", "Video Direction",
690             "Video direction: rotation and flipping",
691             GST_TYPE_VIDEO_ORIENTATION_METHOD, GST_VIDEO_ORIENTATION_IDENTITY,
692             common_flags));
693   }
694
695   return TRUE;
696 }
697
698 /**
699  * GstVaDeinterlaceMethods:
700  * @GST_VA_DEINTERLACE_BOB: Interpolating missing lines by using the
701  *   adjacent lines.
702  * @GST_VA_DEINTERLACE_WEAVE: Show both fields per frame. (don't use)
703  * @GST_VA_DEINTERLACE_ADAPTIVE: Interpolating missing lines by using
704  *   spatial/temporal references.
705  * @GST_VA_DEINTERLACE_COMPENSATED: Recreating missing lines by using
706  *   motion vector.
707  *
708  * Since: 1.20
709  */
710 /* *INDENT-OFF* */
711 static const GEnumValue di_desc[] = {
712   [GST_VA_DEINTERLACE_BOB] =
713       { VAProcDeinterlacingBob,
714         "Bob: Interpolating missing lines by using the adjacent lines.", "bob" },
715   [GST_VA_DEINTERLACE_WEAVE] =
716       { VAProcDeinterlacingWeave, "Weave: Show both fields per frame. (don't use)",
717         "weave" },
718   [GST_VA_DEINTERLACE_ADAPTIVE] =
719       { VAProcDeinterlacingMotionAdaptive,
720         "Adaptive: Interpolating missing lines by using spatial/temporal references.",
721         "adaptive" },
722   [GST_VA_DEINTERLACE_COMPENSATED] =
723       { VAProcDeinterlacingMotionCompensated,
724         "Compensation: Recreating missing lines by using motion vector.",
725         "compensated" },
726 };
727 /* *INDENT-ON* */
728
729 static GType
730 gst_va_deinterlace_methods_get_type (guint num_caps,
731     const VAProcFilterCapDeinterlacing * caps)
732 {
733   guint i, j = 0;
734   static GType deinterlace_methods_type = 0;
735   static GEnumValue methods_types[VAProcDeinterlacingCount];
736
737   if (deinterlace_methods_type > 0)
738     return deinterlace_methods_type;
739
740   for (i = 0; i < num_caps; i++) {
741     if (caps[i].type > VAProcDeinterlacingNone
742         && caps[i].type < VAProcDeinterlacingCount)
743       methods_types[j++] = di_desc[caps[i].type];
744   }
745
746   /* *INDENT-OFF* */
747   methods_types[j] = (GEnumValue) { 0, NULL, NULL };
748   /* *INDENT-ON* */
749
750   deinterlace_methods_type = g_enum_register_static ("GstVaDeinterlaceMethods",
751       (const GEnumValue *) methods_types);
752
753   return deinterlace_methods_type;
754 }
755
756 gboolean
757 gst_va_filter_install_deinterlace_properties (GstVaFilter * self,
758     GObjectClass * klass)
759 {
760   GType type;
761   guint i;
762   const GParamFlags common_flags = G_PARAM_READWRITE
763       | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_PLAYING;
764
765   g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
766
767   if (!gst_va_filter_is_open (self))
768     return FALSE;
769
770   if (!gst_va_filter_ensure_filters (self))
771     return FALSE;
772
773   for (i = 0; i < self->available_filters->len; i++) {
774     const struct VaFilter *filter =
775         &g_array_index (self->available_filters, struct VaFilter, i);
776
777     if (filter->type == VAProcFilterDeinterlacing) {
778       guint i, default_method = 0;
779       const VAProcFilterCapDeinterlacing *caps = filter->caps.deint;
780       if (!caps)
781         break;
782
783       /* use the first method in the list as default */
784       for (i = 0; i < filter->num_caps; i++) {
785         if (caps[i].type > VAProcDeinterlacingNone
786             && caps[i].type < VAProcDeinterlacingCount) {
787           default_method = caps[i].type;
788           break;
789         }
790       }
791
792       if (default_method == 0)
793         break;
794
795       type = gst_va_deinterlace_methods_get_type (filter->num_caps, caps);
796       gst_type_mark_as_plugin_api (type, 0);
797
798       /**
799        * GstVaDeinterlace:method
800        *
801        * Selects the different deinterlacing algorithms that can be used.
802        *
803        * It depends on the driver the number of available algorithms,
804        * and they provide different quality and different processing
805        * consumption.
806        *
807        * Since: 1.20
808        */
809       g_object_class_install_property (klass,
810           GST_VA_FILTER_PROP_DEINTERLACE_METHOD,
811           g_param_spec_enum ("method", "Method", "Deinterlace Method",
812               type, default_method, common_flags));
813
814       return TRUE;
815     }
816   }
817
818   return FALSE;
819 }
820
821 gboolean
822 gst_va_filter_has_filter (GstVaFilter * self, VAProcFilterType type)
823 {
824   guint i;
825
826   g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
827
828   if (!gst_va_filter_is_open (self))
829     return FALSE;
830
831   if (!gst_va_filter_ensure_filters (self))
832     return FALSE;
833
834   for (i = 0; i < self->available_filters->len; i++) {
835     const struct VaFilter *filter =
836         &g_array_index (self->available_filters, struct VaFilter, i);
837
838     if (filter->type == type)
839       return TRUE;
840   }
841
842   return FALSE;
843 }
844
845 const gpointer
846 gst_va_filter_get_filter_caps (GstVaFilter * self, VAProcFilterType type,
847     guint * num_caps)
848 {
849   struct VaFilter *filter = NULL;
850   /* *INDENT-OFF* */
851   static const VAProcFilterCap i965_ste_caps = {
852     .range = {
853       .min_value = 0.0,
854       .max_value = 1.0,
855       .default_value = 0.0,
856       .step = 1.0,
857     },
858   };
859   /* *INDENT-ON* */
860   gpointer ret = NULL;
861   guint i;
862
863   if (!gst_va_filter_is_open (self))
864     return FALSE;
865
866   if (!gst_va_filter_ensure_filters (self))
867     return FALSE;
868
869   GST_OBJECT_LOCK (self);
870   for (i = 0; i < self->available_filters->len; i++) {
871     filter = &g_array_index (self->available_filters, struct VaFilter, i);
872
873     if (filter->type == type) {
874       if (filter->num_caps > 0)
875         ret = &filter->caps;
876       else if (type == VAProcFilterSkinToneEnhancement && filter->num_caps == 0)
877         ret = (gpointer) & i965_ste_caps;
878       break;
879     }
880   }
881
882   if (ret && filter && num_caps)
883     *num_caps = filter->num_caps;
884   GST_OBJECT_UNLOCK (self);
885
886   return ret;
887 }
888
889 guint32
890 gst_va_filter_get_mem_types (GstVaFilter * self)
891 {
892   guint32 ret;
893
894   g_return_val_if_fail (GST_IS_VA_FILTER (self), 0);
895
896   GST_OBJECT_LOCK (self);
897   ret = self->mem_types;
898   GST_OBJECT_UNLOCK (self);
899
900   return ret;
901 }
902
903 GArray *
904 gst_va_filter_get_surface_formats (GstVaFilter * self)
905 {
906   GArray *ret;
907
908   g_return_val_if_fail (GST_IS_VA_FILTER (self), NULL);
909
910   GST_OBJECT_LOCK (self);
911   ret = self->surface_formats ? g_array_ref (self->surface_formats) : NULL;
912   GST_OBJECT_UNLOCK (self);
913
914   return ret;
915 }
916
917 gboolean
918 gst_va_filter_set_scale_method (GstVaFilter * self, guint32 method)
919 {
920   g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
921
922   GST_OBJECT_LOCK (self);
923   self->scale_method = method;
924   GST_OBJECT_UNLOCK (self);
925
926   return TRUE;
927 }
928
929 static gboolean
930 _from_video_orientation_method (GstVideoOrientationMethod orientation,
931     guint * mirror, guint * rotation)
932 {
933   switch (orientation) {
934     case GST_VIDEO_ORIENTATION_IDENTITY:
935       *mirror = VA_MIRROR_NONE;
936       *rotation = VA_ROTATION_NONE;
937       break;
938     case GST_VIDEO_ORIENTATION_HORIZ:
939       *mirror = VA_MIRROR_HORIZONTAL;
940       *rotation = VA_ROTATION_NONE;
941       break;
942     case GST_VIDEO_ORIENTATION_VERT:
943       *mirror = VA_MIRROR_VERTICAL;
944       *rotation = VA_ROTATION_NONE;
945       break;
946     case GST_VIDEO_ORIENTATION_90R:
947       *mirror = VA_MIRROR_NONE;
948       *rotation = VA_ROTATION_90;
949       break;
950     case GST_VIDEO_ORIENTATION_180:
951       *mirror = VA_MIRROR_NONE;
952       *rotation = VA_ROTATION_180;
953       break;
954     case GST_VIDEO_ORIENTATION_90L:
955       *mirror = VA_MIRROR_NONE;
956       *rotation = VA_ROTATION_270;
957       break;
958     case GST_VIDEO_ORIENTATION_UL_LR:
959       *mirror = VA_MIRROR_HORIZONTAL;
960       *rotation = VA_ROTATION_90;
961       break;
962     case GST_VIDEO_ORIENTATION_UR_LL:
963       *mirror = VA_MIRROR_VERTICAL;
964       *rotation = VA_ROTATION_90;
965       break;
966     default:
967       return FALSE;
968       break;
969   }
970
971   return TRUE;
972 }
973
974 gboolean
975 gst_va_filter_set_orientation (GstVaFilter * self,
976     GstVideoOrientationMethod orientation)
977 {
978   guint32 mirror = VA_MIRROR_NONE, rotation = VA_ROTATION_NONE;
979   guint32 mirror_flags, rotation_flags;
980
981   if (!gst_va_filter_is_open (self))
982     return FALSE;
983
984   if (!_from_video_orientation_method (orientation, &mirror, &rotation))
985     return FALSE;
986
987   GST_OBJECT_LOCK (self);
988   mirror_flags = self->pipeline_caps.mirror_flags;
989   GST_OBJECT_UNLOCK (self);
990
991   if (mirror != VA_MIRROR_NONE && !(mirror_flags & mirror))
992     return FALSE;
993
994   GST_OBJECT_LOCK (self);
995   rotation_flags = self->pipeline_caps.rotation_flags;
996   GST_OBJECT_UNLOCK (self);
997
998   if (rotation != VA_ROTATION_NONE && !(rotation_flags & (1 << rotation)))
999     return FALSE;
1000
1001   GST_OBJECT_LOCK (self);
1002   self->orientation = orientation;
1003   self->mirror = mirror;
1004   self->rotation = rotation;
1005   GST_OBJECT_UNLOCK (self);
1006
1007   return TRUE;
1008 }
1009
1010 GstVideoOrientationMethod
1011 gst_va_filter_get_orientation (GstVaFilter * self)
1012 {
1013   GstVideoOrientationMethod ret;
1014
1015   GST_OBJECT_LOCK (self);
1016   ret = self->orientation;
1017   GST_OBJECT_UNLOCK (self);
1018
1019   return ret;
1020 }
1021
1022 void
1023 gst_va_filter_enable_cropping (GstVaFilter * self, gboolean cropping)
1024 {
1025   GST_OBJECT_LOCK (self);
1026   if (cropping != self->crop_enabled)
1027     self->crop_enabled = cropping;
1028   GST_OBJECT_UNLOCK (self);
1029 }
1030
1031 static inline GstCaps *
1032 _create_base_caps (GstVaFilter * self)
1033 {
1034   return gst_caps_new_simple ("video/x-raw", "width", GST_TYPE_INT_RANGE,
1035       self->min_width, self->max_width, "height", GST_TYPE_INT_RANGE,
1036       self->min_height, self->max_height, NULL);
1037 }
1038
1039 GstCaps *
1040 gst_va_filter_get_caps (GstVaFilter * self)
1041 {
1042   GArray *surface_formats = NULL, *image_formats = NULL;
1043   GstCaps *caps, *base_caps, *feature_caps;
1044   GstCapsFeatures *features;
1045   guint32 mem_types;
1046
1047   g_return_val_if_fail (GST_IS_VA_FILTER (self), NULL);
1048
1049   if (!gst_va_filter_is_open (self))
1050     return NULL;
1051
1052   surface_formats = gst_va_filter_get_surface_formats (self);
1053   if (!surface_formats)
1054     return NULL;
1055
1056   base_caps = _create_base_caps (self);
1057
1058   if (!gst_caps_set_format_array (base_caps, surface_formats))
1059     goto fail;
1060
1061   g_array_unref (surface_formats);
1062
1063   caps = gst_caps_new_empty ();
1064
1065   mem_types = gst_va_filter_get_mem_types (self);
1066
1067   if (mem_types & VA_SURFACE_ATTRIB_MEM_TYPE_VA) {
1068     feature_caps = gst_caps_copy (base_caps);
1069     features = gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_VA);
1070     gst_caps_set_features_simple (feature_caps, features);
1071     caps = gst_caps_merge (caps, feature_caps);
1072   }
1073   if (mem_types & VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME
1074       || mem_types & VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2) {
1075     feature_caps = gst_caps_copy (base_caps);
1076     features = gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_DMABUF);
1077     gst_caps_set_features_simple (feature_caps, features);
1078     caps = gst_caps_merge (caps, feature_caps);
1079   }
1080
1081   gst_caps_unref (base_caps);
1082
1083   base_caps = _create_base_caps (self);
1084
1085   GST_OBJECT_LOCK (self);
1086   image_formats =
1087       self->image_formats ? g_array_ref (self->image_formats) : NULL;
1088   GST_OBJECT_UNLOCK (self);
1089
1090   if (image_formats) {
1091     if (!gst_caps_set_format_array (base_caps, image_formats))
1092       goto fail;
1093     g_array_unref (image_formats);
1094   }
1095
1096   return gst_caps_merge (caps, base_caps);
1097
1098 fail:
1099   {
1100     g_clear_pointer (&surface_formats, g_array_unref);
1101     g_clear_pointer (&image_formats, g_array_unref);
1102     gst_caps_unref (base_caps);
1103     return NULL;
1104   }
1105 }
1106
1107 /* from va_vpp.h */
1108 /* *INDENT-OFF* */
1109 static const struct ColorPropertiesMap
1110 {
1111   VAProcColorStandardType standard;
1112   guint8 primaries;
1113   guint8 transfer;
1114   guint8 matrix;
1115 } color_properties_map[] = {
1116   { VAProcColorStandardBT601, 5,  6,  5 },
1117   { VAProcColorStandardBT601, 6,  6,  6 },
1118   { VAProcColorStandardBT709, 1,  1,  1 },
1119   { VAProcColorStandardBT470M, 4,  4,  4 },
1120   { VAProcColorStandardBT470BG, 5,  5,  5 },
1121   { VAProcColorStandardSMPTE170M, 6,  6,  6 },
1122   { VAProcColorStandardSMPTE240M, 7,  7,  7 },
1123   { VAProcColorStandardGenericFilm, 8,  1,  1 },
1124   { VAProcColorStandardSRGB, 1, 13,  0 },
1125   /* { VAProcColorStandardSTRGB, ?, ?, ? }, */
1126   { VAProcColorStandardXVYCC601, 1, 11,  5 },
1127   { VAProcColorStandardXVYCC709, 1, 11,  1 },
1128   { VAProcColorStandardBT2020, 9, 14,  9 },
1129 };
1130   /* *INDENT-ON* */
1131
1132 static guint8
1133 _get_chroma_siting (GstVideoChromaSite chrome_site)
1134 {
1135   /* *INDENT-OFF* */
1136   static const struct ChromaSiteMap {
1137     GstVideoChromaSite gst;
1138     guint8 va;
1139   } chroma_site_map[] = {
1140     { GST_VIDEO_CHROMA_SITE_UNKNOWN, VA_CHROMA_SITING_UNKNOWN },
1141     { GST_VIDEO_CHROMA_SITE_NONE, VA_CHROMA_SITING_VERTICAL_CENTER
1142                                   | VA_CHROMA_SITING_HORIZONTAL_CENTER },
1143     { GST_VIDEO_CHROMA_SITE_H_COSITED, VA_CHROMA_SITING_VERTICAL_CENTER
1144                                        | VA_CHROMA_SITING_HORIZONTAL_LEFT },
1145     { GST_VIDEO_CHROMA_SITE_V_COSITED, VA_CHROMA_SITING_VERTICAL_TOP
1146                                        | VA_CHROMA_SITING_VERTICAL_BOTTOM },
1147     { GST_VIDEO_CHROMA_SITE_COSITED, VA_CHROMA_SITING_VERTICAL_CENTER
1148                                      | VA_CHROMA_SITING_HORIZONTAL_LEFT
1149                                      | VA_CHROMA_SITING_VERTICAL_TOP
1150                                      | VA_CHROMA_SITING_VERTICAL_BOTTOM },
1151     { GST_VIDEO_CHROMA_SITE_JPEG, VA_CHROMA_SITING_VERTICAL_CENTER
1152                                   | VA_CHROMA_SITING_HORIZONTAL_CENTER  },
1153     { GST_VIDEO_CHROMA_SITE_MPEG2, VA_CHROMA_SITING_VERTICAL_CENTER
1154                                    | VA_CHROMA_SITING_HORIZONTAL_LEFT },
1155     { GST_VIDEO_CHROMA_SITE_DV, VA_CHROMA_SITING_VERTICAL_TOP
1156                                 | VA_CHROMA_SITING_HORIZONTAL_LEFT },
1157   };
1158   /* *INDENT-ON* */
1159   guint i;
1160
1161   for (i = 0; i < G_N_ELEMENTS (chroma_site_map); i++) {
1162     if (chrome_site == chroma_site_map[i].gst)
1163       return chroma_site_map[i].va;
1164   }
1165
1166   return VA_CHROMA_SITING_UNKNOWN;
1167 }
1168
1169 static guint8
1170 _get_color_range (GstVideoColorRange range)
1171 {
1172   /* *INDENT-OFF* */
1173   static const struct ColorRangeMap {
1174     GstVideoColorRange gst;
1175     guint8 va;
1176   } color_range_map[] = {
1177     { GST_VIDEO_COLOR_RANGE_UNKNOWN, VA_SOURCE_RANGE_UNKNOWN },
1178     { GST_VIDEO_COLOR_RANGE_0_255, VA_SOURCE_RANGE_FULL },
1179     { GST_VIDEO_COLOR_RANGE_16_235, VA_SOURCE_RANGE_REDUCED },
1180   };
1181   /* *INDENT-ON* */
1182   guint i;
1183
1184   for (i = 0; i < G_N_ELEMENTS (color_range_map); i++) {
1185     if (range == color_range_map[i].gst)
1186       return color_range_map[i].va;
1187   }
1188
1189   return VA_SOURCE_RANGE_UNKNOWN;
1190 }
1191
1192 static VAProcColorStandardType
1193 _gst_video_colorimetry_to_va (const GstVideoColorimetry * const colorimetry)
1194 {
1195   if (!colorimetry
1196       || colorimetry->primaries == GST_VIDEO_COLOR_PRIMARIES_UNKNOWN)
1197     return VAProcColorStandardNone;
1198
1199   if (gst_video_colorimetry_matches (colorimetry, GST_VIDEO_COLORIMETRY_BT709))
1200     return VAProcColorStandardBT709;
1201
1202   /* NOTE: VAProcColorStandardBT2020 in VAAPI is the same as
1203    * GST_VIDEO_COLORIMETRY_BT2020_10 in gstreamer. */
1204   if (gst_video_colorimetry_matches (colorimetry,
1205           GST_VIDEO_COLORIMETRY_BT2020_10) ||
1206       gst_video_colorimetry_matches (colorimetry, GST_VIDEO_COLORIMETRY_BT2020))
1207     return VAProcColorStandardBT2020;
1208
1209   if (gst_video_colorimetry_matches (colorimetry, GST_VIDEO_COLORIMETRY_BT601))
1210     return VAProcColorStandardBT601;
1211
1212   if (gst_video_colorimetry_matches (colorimetry,
1213           GST_VIDEO_COLORIMETRY_SMPTE240M))
1214     return VAProcColorStandardSMPTE240M;
1215
1216   if (gst_video_colorimetry_matches (colorimetry, GST_VIDEO_COLORIMETRY_SRGB))
1217     return VAProcColorStandardSRGB;
1218
1219   return VAProcColorStandardNone;
1220 }
1221
1222 static void
1223 _config_color_properties (VAProcColorStandardType * std,
1224     VAProcColorProperties * props, const GstVideoInfo * info,
1225     VAProcColorStandardType * standards, guint32 num_standards)
1226 {
1227   GstVideoColorimetry colorimetry = GST_VIDEO_INFO_COLORIMETRY (info);
1228   VAProcColorStandardType best;
1229   gboolean has_explicit;
1230   guint i, j, k;
1231   gint score, bestscore = -1, worstscore;
1232
1233   best = _gst_video_colorimetry_to_va (&colorimetry);
1234
1235   has_explicit = FALSE;
1236   for (i = 0; i < num_standards; i++) {
1237     /* Find the exact match standard. */
1238     if (standards[i] != VAProcColorStandardNone && standards[i] == best)
1239       break;
1240
1241     if (standards[i] == VAProcColorStandardExplicit)
1242       has_explicit = TRUE;
1243   }
1244
1245   if (i < num_standards) {
1246     *std = best;
1247     goto set_properties;
1248   } else if (has_explicit) {
1249     *std = VAProcColorStandardExplicit;
1250     goto set_properties;
1251   }
1252
1253   worstscore = 4 * (colorimetry.matrix != GST_VIDEO_COLOR_MATRIX_UNKNOWN
1254       && colorimetry.matrix != GST_VIDEO_COLOR_MATRIX_RGB)
1255       + 2 * (colorimetry.transfer != GST_VIDEO_TRANSFER_UNKNOWN)
1256       + (colorimetry.primaries != GST_VIDEO_COLOR_PRIMARIES_UNKNOWN);
1257
1258   if (worstscore == 0) {
1259     /* No properties specified, there's not a useful choice. */
1260     *std = VAProcColorStandardNone;
1261     *props = (VAProcColorProperties) {
1262     };
1263
1264     return;
1265   }
1266
1267   best = VAProcColorStandardNone;
1268   k = -1;
1269   for (i = 0; i < num_standards; i++) {
1270     for (j = 0; j < G_N_ELEMENTS (color_properties_map); j++) {
1271       if (color_properties_map[j].standard != standards[i])
1272         continue;
1273
1274       score = 0;
1275       if (colorimetry.matrix != GST_VIDEO_COLOR_MATRIX_UNKNOWN
1276           && colorimetry.matrix != GST_VIDEO_COLOR_MATRIX_RGB)
1277         score += 4 * (colorimetry.matrix != color_properties_map[j].matrix);
1278       if (colorimetry.transfer != GST_VIDEO_TRANSFER_UNKNOWN)
1279         score += 2 * (colorimetry.transfer != color_properties_map[j].transfer);
1280       if (colorimetry.primaries != GST_VIDEO_COLOR_PRIMARIES_UNKNOWN)
1281         score += (colorimetry.primaries != color_properties_map[j].primaries);
1282
1283       if (score < worstscore && (bestscore == -1 || score < bestscore)) {
1284         bestscore = score;
1285         best = color_properties_map[j].standard;
1286         k = j;
1287       }
1288     }
1289   }
1290
1291   if (best != VAProcColorStandardNone) {
1292     *std = best;
1293     colorimetry.matrix = color_properties_map[k].matrix;
1294     colorimetry.transfer = color_properties_map[k].transfer;
1295     colorimetry.primaries = color_properties_map[k].primaries;
1296   }
1297
1298 set_properties:
1299   /* *INDENT-OFF* */
1300   *props = (VAProcColorProperties) {
1301     .chroma_sample_location =
1302         _get_chroma_siting (GST_VIDEO_INFO_CHROMA_SITE (info)),
1303     .color_range = _get_color_range (colorimetry.range),
1304     .colour_primaries =
1305         gst_video_color_primaries_to_iso (colorimetry.primaries),
1306     .transfer_characteristics =
1307         gst_video_transfer_function_to_iso (colorimetry.transfer),
1308     .matrix_coefficients =
1309         gst_video_color_matrix_to_iso (colorimetry.matrix),
1310   };
1311   /* *INDENT-ON* */
1312 }
1313
1314 gboolean
1315 gst_va_filter_set_video_info (GstVaFilter * self, GstVideoInfo * in_info,
1316     GstVideoInfo * out_info)
1317 {
1318   g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
1319   g_return_val_if_fail (out_info && in_info, FALSE);
1320
1321   if (!gst_va_filter_is_open (self))
1322     return FALSE;
1323
1324   GST_OBJECT_LOCK (self);
1325   /* *INDENT-OFF* */
1326   self->input_region = (VARectangle) {
1327     .width = GST_VIDEO_INFO_WIDTH (in_info),
1328     .height = GST_VIDEO_INFO_HEIGHT (in_info),
1329   };
1330
1331   self->output_region = (VARectangle) {
1332     .width = GST_VIDEO_INFO_WIDTH (out_info),
1333     .height = GST_VIDEO_INFO_HEIGHT (out_info),
1334   };
1335   /* *INDENT-ON* */
1336
1337   _config_color_properties (&self->input_color_standard,
1338       &self->input_color_properties, in_info,
1339       self->pipeline_caps.input_color_standards,
1340       self->pipeline_caps.num_input_color_standards);
1341   _config_color_properties (&self->output_color_standard,
1342       &self->output_color_properties, out_info,
1343       self->pipeline_caps.output_color_standards,
1344       self->pipeline_caps.num_output_color_standards);
1345   GST_OBJECT_UNLOCK (self);
1346
1347   return TRUE;
1348 }
1349
1350 static gboolean
1351 _query_pipeline_caps (GstVaFilter * self, GArray * filters,
1352     VAProcPipelineCaps * caps)
1353 {
1354   VABufferID *va_filters = NULL;
1355   VADisplay dpy;
1356   VAStatus status;
1357   guint32 num_filters = 0;
1358
1359   GST_OBJECT_LOCK (self);
1360   if (filters) {
1361     num_filters = filters->len;
1362     va_filters = (num_filters > 0) ? (VABufferID *) filters->data : NULL;
1363   }
1364   GST_OBJECT_UNLOCK (self);
1365
1366   dpy = gst_va_display_get_va_dpy (self->display);
1367
1368   status = vaQueryVideoProcPipelineCaps (dpy, self->context, va_filters,
1369       num_filters, caps);
1370
1371   if (status != VA_STATUS_SUCCESS) {
1372     GST_ERROR_OBJECT (self, "vaQueryVideoProcPipelineCaps: %s",
1373         vaErrorStr (status));
1374     return FALSE;
1375   }
1376
1377   return TRUE;
1378 }
1379
1380 gboolean
1381 gst_va_filter_add_deinterlace_buffer (GstVaFilter * self,
1382     VAProcDeinterlacingType method, guint32 * forward, guint32 * backward)
1383 {
1384   GArray *filters = NULL;
1385   VAProcFilterParameterBufferDeinterlacing params = {
1386     .type = VAProcFilterDeinterlacing,
1387     .algorithm = method,
1388   };
1389   VAProcPipelineCaps pipeline_caps = { 0, };
1390   gboolean ret;
1391
1392   g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
1393
1394   if (!gst_va_filter_is_open (self))
1395     return FALSE;
1396
1397   if (!(method != VAProcDeinterlacingNone
1398           && method != VAProcDeinterlacingCount))
1399     return FALSE;
1400
1401   if (!gst_va_filter_add_filter_buffer (self, &params, sizeof (params), 1))
1402     return FALSE;
1403
1404   GST_OBJECT_LOCK (self);
1405   if (self->filters)
1406     filters = g_array_ref (self->filters);
1407   GST_OBJECT_UNLOCK (self);
1408   ret = _query_pipeline_caps (self, filters, &pipeline_caps);
1409   if (filters)
1410     g_array_unref (filters);
1411   if (!ret)
1412     return FALSE;
1413
1414   if (forward)
1415     *forward = pipeline_caps.num_forward_references;
1416   if (backward)
1417     *backward = pipeline_caps.num_backward_references;
1418
1419   return TRUE;
1420 }
1421
1422 #ifndef GST_DISABLE_GST_DEBUG
1423 static const gchar *
1424 get_va_filter_name (gpointer data)
1425 {
1426   VAProcFilterType type = ((VAProcFilterParameterBuffer *) data)->type;
1427   const struct VaFilterCapMap *m = gst_va_filter_get_filter_cap (type);
1428
1429   return m ? m->name : "Unknown";
1430 }
1431 #endif
1432
1433 gboolean
1434 gst_va_filter_add_filter_buffer (GstVaFilter * self, gpointer data, gsize size,
1435     guint num)
1436 {
1437   VABufferID buffer;
1438   VADisplay dpy;
1439   VAStatus status;
1440
1441   g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
1442   g_return_val_if_fail (data && size > 0, FALSE);
1443
1444   if (!gst_va_filter_is_open (self))
1445     return FALSE;
1446
1447   dpy = gst_va_display_get_va_dpy (self->display);
1448   status = vaCreateBuffer (dpy, self->context, VAProcFilterParameterBufferType,
1449       size, num, data, &buffer);
1450   if (status != VA_STATUS_SUCCESS) {
1451     GST_ERROR_OBJECT (self, "vaCreateBuffer: %s", vaErrorStr (status));
1452     return FALSE;
1453   }
1454
1455   GST_DEBUG_OBJECT (self, "Added filter: %s", get_va_filter_name (data));
1456
1457   /* lazy creation */
1458   GST_OBJECT_LOCK (self);
1459   if (!self->filters)
1460     self->filters = g_array_sized_new (FALSE, FALSE, sizeof (VABufferID), 16);
1461
1462   g_array_append_val (self->filters, buffer);
1463   GST_OBJECT_UNLOCK (self);
1464
1465   return TRUE;
1466 }
1467
1468 static gboolean
1469 _destroy_filters_unlocked (GstVaFilter * self)
1470 {
1471   VABufferID buffer;
1472   VADisplay dpy;
1473   VAStatus status;
1474   gboolean ret = TRUE;
1475   guint i;
1476
1477   GST_TRACE_OBJECT (self, "Destroying %u filter buffers", self->filters->len);
1478
1479   dpy = gst_va_display_get_va_dpy (self->display);
1480
1481   for (i = 0; i < self->filters->len; i++) {
1482     buffer = g_array_index (self->filters, VABufferID, i);
1483
1484     status = vaDestroyBuffer (dpy, buffer);
1485     if (status != VA_STATUS_SUCCESS) {
1486       ret = FALSE;
1487       GST_WARNING_OBJECT (self, "Failed to destroy filter buffer: %s",
1488           vaErrorStr (status));
1489     }
1490   }
1491
1492   self->filters = g_array_set_size (self->filters, 0);
1493
1494   return ret;
1495 }
1496
1497 gboolean
1498 gst_va_filter_drop_filter_buffers (GstVaFilter * self)
1499 {
1500   gboolean ret = TRUE;
1501
1502   g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
1503
1504   GST_OBJECT_LOCK (self);
1505   if (self->filters)
1506     ret = _destroy_filters_unlocked (self);
1507   GST_OBJECT_UNLOCK (self);
1508
1509   return ret;
1510 }
1511
1512 static VASurfaceID
1513 _get_surface_from_buffer (GstVaFilter * self, GstBuffer * buffer)
1514 {
1515   VASurfaceID surface = VA_INVALID_ID;
1516
1517   if (buffer)
1518     surface = gst_va_buffer_get_surface (buffer);
1519
1520   if (surface != VA_INVALID_ID) {
1521     /* @FIXME: in gallium vaQuerySurfaceStatus only seems to work with
1522      * encoder's surfaces */
1523     if (!GST_VA_DISPLAY_IS_IMPLEMENTATION (self->display, MESA_GALLIUM))
1524       if (!va_check_surface (self->display, surface))
1525         surface = VA_INVALID_ID;
1526   }
1527
1528   return surface;
1529 }
1530
1531 static gboolean
1532 _fill_va_sample (GstVaFilter * self, GstVaSample * sample,
1533     GstPadDirection direction)
1534 {
1535   GstVideoCropMeta *crop = NULL;
1536
1537   sample->surface = _get_surface_from_buffer (self, sample->buffer);
1538   if (sample->surface == VA_INVALID_ID)
1539     return FALSE;
1540
1541   /* XXX: cropping occurs only in input frames */
1542   if (direction == GST_PAD_SRC) {
1543     GST_OBJECT_LOCK (self);
1544     sample->rect = self->output_region;
1545     sample->rect.x = sample->borders_w / 2;
1546     sample->rect.y = sample->borders_h / 2;
1547     sample->rect.width -= sample->borders_w;
1548     sample->rect.height -= sample->borders_h;
1549     GST_OBJECT_UNLOCK (self);
1550
1551     return TRUE;
1552   }
1553
1554   /* if buffer has crop meta, its real size is in video meta */
1555   if (sample->buffer)
1556     crop = gst_buffer_get_video_crop_meta (sample->buffer);
1557
1558   GST_OBJECT_LOCK (self);
1559   if (crop && self->crop_enabled) {
1560     /* *INDENT-OFF* */
1561     sample->rect = (VARectangle) {
1562       .x = crop->x,
1563       .y = crop->y,
1564       .width = crop->width,
1565       .height = crop->height
1566     };
1567     /* *INDENT-ON* */
1568   } else {
1569     sample->rect = self->input_region;
1570   }
1571   GST_OBJECT_UNLOCK (self);
1572
1573   return TRUE;
1574 }
1575
1576 static gboolean
1577 _create_pipeline_buffer (GstVaFilter * self, GstVaSample * src,
1578     GstVaSample * dst, GArray * filters, VABufferID * buffer)
1579 {
1580   VADisplay dpy;
1581   VAStatus status;
1582   VABufferID *va_filters = NULL;
1583   VAProcPipelineParameterBuffer params;
1584   guint32 num_filters = 0;
1585
1586   GST_OBJECT_LOCK (self);
1587
1588   /* *INDENT-OFF* */
1589   if (filters) {
1590     num_filters = filters->len;
1591     va_filters = (num_filters > 0) ? (VABufferID *) filters->data : NULL;
1592   }
1593   params = (VAProcPipelineParameterBuffer) {
1594     .surface = src->surface,
1595     .surface_region = &src->rect,
1596     .surface_color_standard = self->input_color_standard,
1597     .output_region = &dst->rect,
1598     .output_background_color = 0xff000000, /* ARGB black */
1599     .output_color_standard = self->output_color_standard,
1600     .filters = va_filters,
1601     .num_filters = num_filters,
1602     .forward_references = src->forward_references,
1603     .num_forward_references = src->num_forward_references,
1604     .backward_references = src->backward_references,
1605     .num_backward_references = src->num_backward_references,
1606     .rotation_state = self->rotation,
1607     .mirror_state = self->mirror,
1608     .input_surface_flag = src->flags,
1609     .output_surface_flag = dst->flags,
1610     .input_color_properties = self->input_color_properties,
1611     .output_color_properties = self->output_color_properties,
1612     .filter_flags = self->scale_method,
1613     /* output to SDR */
1614     .output_hdr_metadata = NULL,
1615   };
1616   /* *INDENT-ON* */
1617
1618   GST_OBJECT_UNLOCK (self);
1619
1620   dpy = gst_va_display_get_va_dpy (self->display);
1621   status = vaCreateBuffer (dpy, self->context,
1622       VAProcPipelineParameterBufferType, sizeof (params), 1, &params, buffer);
1623   if (status != VA_STATUS_SUCCESS) {
1624     GST_ERROR_OBJECT (self, "vaCreateBuffer: %s", vaErrorStr (status));
1625     return FALSE;
1626   }
1627
1628   GST_TRACE_OBJECT (self, "Created VABufferID %#x with %u filters: "
1629       "src %#x / dst %#x", *buffer, num_filters, src->surface, dst->surface);
1630
1631   return TRUE;
1632 }
1633
1634 gboolean
1635 gst_va_filter_process (GstVaFilter * self, GstVaSample * src, GstVaSample * dst)
1636 {
1637   GArray *filters = NULL;
1638   VABufferID buffer;
1639   VADisplay dpy;
1640   VAProcPipelineCaps pipeline_caps = { 0, };
1641   VAStatus status;
1642   gboolean ret = FALSE;
1643
1644   g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
1645   g_return_val_if_fail (src, FALSE);
1646   g_return_val_if_fail (dst, FALSE);
1647
1648   if (!gst_va_filter_is_open (self))
1649     return FALSE;
1650
1651   if (!(_fill_va_sample (self, src, GST_PAD_SINK)
1652           && _fill_va_sample (self, dst, GST_PAD_SRC)))
1653     return FALSE;
1654
1655   GST_OBJECT_LOCK (self);
1656   if (self->filters)
1657     filters = g_array_ref (self->filters);
1658   GST_OBJECT_UNLOCK (self);
1659
1660   if (!_query_pipeline_caps (self, filters, &pipeline_caps))
1661     return FALSE;
1662
1663   if (!_create_pipeline_buffer (self, src, dst, filters, &buffer))
1664     return FALSE;
1665
1666   if (filters)
1667     g_array_unref (filters);
1668
1669   dpy = gst_va_display_get_va_dpy (self->display);
1670
1671   status = vaBeginPicture (dpy, self->context, dst->surface);
1672   if (status != VA_STATUS_SUCCESS) {
1673     GST_ERROR_OBJECT (self, "vaBeginPicture: %s", vaErrorStr (status));
1674     return FALSE;
1675   }
1676
1677   status = vaRenderPicture (dpy, self->context, &buffer, 1);
1678   if (status != VA_STATUS_SUCCESS) {
1679     GST_ERROR_OBJECT (self, "vaRenderPicture: %s with buffer %#x",
1680         vaErrorStr (status), buffer);
1681     goto fail_end_pic;
1682   }
1683
1684   status = vaEndPicture (dpy, self->context);
1685   if (status != VA_STATUS_SUCCESS) {
1686     GST_ERROR_OBJECT (self, "vaEndPicture: %s", vaErrorStr (status));
1687     goto bail;
1688   }
1689
1690   ret = TRUE;
1691
1692 bail:
1693   status = vaDestroyBuffer (dpy, buffer);
1694   if (status != VA_STATUS_SUCCESS) {
1695     GST_WARNING_OBJECT (self, "Failed to destroy pipeline buffer: %s",
1696         vaErrorStr (status));
1697   }
1698
1699   return ret;
1700
1701 fail_end_pic:
1702   {
1703     status = vaEndPicture (dpy, self->context);
1704     if (status != VA_STATUS_SUCCESS)
1705       GST_ERROR_OBJECT (self, "vaEndPicture: %s", vaErrorStr (status));
1706     goto bail;
1707   }
1708 }
1709
1710 gboolean
1711 gst_va_filter_has_compose (GstVaFilter * self)
1712 {
1713   g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
1714
1715   if (!gst_va_filter_is_open (self))
1716     return FALSE;
1717
1718   /* HACK(uartie): i965 can't do composition */
1719   if (GST_VA_DISPLAY_IS_IMPLEMENTATION (self->display, INTEL_I965))
1720     return FALSE;
1721
1722   /* some drivers can compose, but may not support blending (e.g. GALLIUM) */
1723 #ifndef GST_DISABLE_GST_DEBUG
1724   if (!(self->pipeline_caps.blend_flags & VA_BLEND_GLOBAL_ALPHA))
1725     GST_WARNING_OBJECT (self, "VPP does not support alpha blending");
1726 #endif
1727
1728   return TRUE;
1729 }
1730
1731 /**
1732  * gst_va_filter_compose:
1733  * @tx: the #GstVaComposeTransaction for input samples and output.
1734  *
1735  * Iterates over all inputs via #GstVaComposeTransaction:next and composes
1736  * them onto the #GstVaComposeTransaction:output.
1737  *
1738  * Only csc, scaling and blending filters are applied during composition.
1739  * All other filters are ignored here.  Use #gst_va_filter_process to apply
1740  * other filters.
1741  *
1742  * Returns: TRUE on successful compose, FALSE otherwise.
1743  *
1744  * Since: 1.22
1745  */
1746 gboolean
1747 gst_va_filter_compose (GstVaFilter * self, GstVaComposeTransaction * tx)
1748 {
1749   VADisplay dpy;
1750   VAStatus status;
1751   VASurfaceID out_surface;
1752   GstVaComposeSample *sample;
1753
1754   g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
1755   g_return_val_if_fail (tx, FALSE);
1756   g_return_val_if_fail (tx->next, FALSE);
1757   g_return_val_if_fail (tx->output, FALSE);
1758
1759   if (!gst_va_filter_is_open (self))
1760     return FALSE;
1761
1762   out_surface = _get_surface_from_buffer (self, tx->output);
1763   if (out_surface == VA_INVALID_ID)
1764     return FALSE;
1765
1766   dpy = gst_va_display_get_va_dpy (self->display);
1767
1768   status = vaBeginPicture (dpy, self->context, out_surface);
1769   if (status != VA_STATUS_SUCCESS) {
1770     GST_ERROR_OBJECT (self, "vaBeginPicture: %s", vaErrorStr (status));
1771     return FALSE;
1772   }
1773
1774   sample = tx->next (tx->user_data);
1775   for (; sample; sample = tx->next (tx->user_data)) {
1776     VAProcPipelineParameterBuffer params = { 0, };
1777     VABufferID buffer;
1778     VASurfaceID in_surface;
1779     VABlendState blend = { 0, };
1780
1781     in_surface = _get_surface_from_buffer (self, sample->buffer);
1782     if (in_surface == VA_INVALID_ID)
1783       return FALSE;
1784
1785     /* (transfer full), unref it */
1786     gst_buffer_unref (sample->buffer);
1787
1788     GST_OBJECT_LOCK (self);
1789     /* *INDENT-OFF* */
1790     params = (VAProcPipelineParameterBuffer) {
1791       .surface = in_surface,
1792       .surface_region = &sample->input_region,
1793       .output_region = &sample->output_region,
1794       .output_background_color = 0xff000000,
1795       .filter_flags = self->scale_method,
1796     };
1797     /* *INDENT-ON* */
1798     GST_OBJECT_UNLOCK (self);
1799
1800     /* only send blend state when sample is not fully opaque */
1801     if ((self->pipeline_caps.blend_flags & VA_BLEND_GLOBAL_ALPHA)
1802         && sample->alpha < 1.0) {
1803       /* *INDENT-OFF* */
1804       blend = (VABlendState) {
1805         .flags = VA_BLEND_GLOBAL_ALPHA,
1806         .global_alpha = sample->alpha,
1807       };
1808       /* *INDENT-ON* */
1809       params.blend_state = &blend;
1810     }
1811
1812     status = vaCreateBuffer (dpy, self->context,
1813         VAProcPipelineParameterBufferType, sizeof (params), 1, &params,
1814         &buffer);
1815     if (status != VA_STATUS_SUCCESS) {
1816       GST_ERROR_OBJECT (self, "vaCreateBuffer: %s", vaErrorStr (status));
1817       goto fail_end_pic;
1818     }
1819
1820     status = vaRenderPicture (dpy, self->context, &buffer, 1);
1821     vaDestroyBuffer (dpy, buffer);
1822     if (status != VA_STATUS_SUCCESS) {
1823       GST_ERROR_OBJECT (self, "vaRenderPicture: %s", vaErrorStr (status));
1824       goto fail_end_pic;
1825     }
1826   }
1827
1828   status = vaEndPicture (dpy, self->context);
1829   if (status != VA_STATUS_SUCCESS) {
1830     GST_ERROR_OBJECT (self, "vaEndPicture: %s", vaErrorStr (status));
1831     return FALSE;
1832   }
1833
1834   return TRUE;
1835
1836 fail_end_pic:
1837   {
1838     status = vaEndPicture (dpy, self->context);
1839     if (status != VA_STATUS_SUCCESS)
1840       GST_ERROR_OBJECT (self, "vaEndPicture: %s", vaErrorStr (status));
1841     return FALSE;
1842   }
1843 }
1844
1845 /**
1846  * gst_va_buffer_get_surface_flags:
1847  * @buffer: the #GstBuffer to check.
1848  * @info: the #GstVideoInfo with info.
1849  *
1850  * Gets the surface flags, related with interlace given @buffer and
1851  * @info.
1852  *
1853  * Returns: VA surface flags.
1854  */
1855 guint32
1856 gst_va_buffer_get_surface_flags (GstBuffer * buffer, GstVideoInfo * info)
1857 {
1858   guint32 surface_flags = 0;
1859
1860   if (GST_VIDEO_INFO_INTERLACE_MODE (info) == GST_VIDEO_INTERLACE_MODE_MIXED
1861       || (GST_VIDEO_INFO_INTERLACE_MODE (info) ==
1862           GST_VIDEO_INTERLACE_MODE_INTERLEAVED
1863           && GST_VIDEO_INFO_FIELD_ORDER (info) ==
1864           GST_VIDEO_FIELD_ORDER_UNKNOWN)) {
1865     if (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_FLAG_INTERLACED)) {
1866       if (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_FLAG_TFF)) {
1867         surface_flags = VA_TOP_FIELD_FIRST;
1868       } else {
1869         surface_flags = VA_BOTTOM_FIELD_FIRST;
1870       }
1871     } else {
1872       surface_flags = VA_FRAME_PICTURE;
1873     }
1874   } else if (GST_VIDEO_INFO_FIELD_ORDER (info) ==
1875       GST_VIDEO_FIELD_ORDER_BOTTOM_FIELD_FIRST) {
1876     surface_flags = VA_BOTTOM_FIELD_FIRST;
1877   } else if (GST_VIDEO_INFO_FIELD_ORDER (info) ==
1878       GST_VIDEO_FIELD_ORDER_TOP_FIELD_FIRST) {
1879     surface_flags = VA_TOP_FIELD_FIRST;
1880   }
1881
1882   return surface_flags;
1883 }
1884
1885 gboolean
1886 gst_va_filter_has_video_format (GstVaFilter * self, GstVideoFormat format,
1887     GstCapsFeatures * feature)
1888 {
1889   guint i;
1890   GstVideoFormat fmt;
1891
1892   g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
1893   g_return_val_if_fail (format != GST_VIDEO_FORMAT_UNKNOWN, FALSE);
1894   g_return_val_if_fail (GST_IS_CAPS_FEATURES (feature)
1895       && !gst_caps_features_is_any (feature), FALSE);
1896
1897
1898   GST_OBJECT_LOCK (self);
1899   for (i = 0; i < self->surface_formats->len; i++) {
1900     fmt = g_array_index (self->surface_formats, GstVideoFormat, i);
1901     if (format == fmt) {
1902       GST_OBJECT_UNLOCK (self);
1903       return TRUE;
1904     }
1905   }
1906   GST_OBJECT_UNLOCK (self);
1907
1908   if (!gst_caps_features_is_equal (feature,
1909           GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY))
1910     return FALSE;
1911
1912   GST_OBJECT_LOCK (self);
1913   for (i = 0; i < self->image_formats->len; i++) {
1914     fmt = g_array_index (self->image_formats, GstVideoFormat, i);
1915     if (format == fmt) {
1916       GST_OBJECT_UNLOCK (self);
1917       return TRUE;
1918     }
1919   }
1920   GST_OBJECT_UNLOCK (self);
1921
1922   return FALSE;
1923 }
1924
1925 /**
1926  * GstVaScaleMethod:
1927  *
1928  * Since: 1.22
1929  */
1930 GType
1931 gst_va_scale_method_get_type (void)
1932 {
1933   static gsize type = 0;
1934   static const GEnumValue values[] = {
1935     {VA_FILTER_SCALING_DEFAULT, "Default scaling method", "default"},
1936     {VA_FILTER_SCALING_FAST, "Fast scaling method", "fast"},
1937     {VA_FILTER_SCALING_HQ, "High quality scaling method", "hq"},
1938     {0, NULL, NULL},
1939   };
1940
1941   if (g_once_init_enter (&type)) {
1942     const GType _type = g_enum_register_static ("GstVaScaleMethod", values);
1943     g_once_init_leave (&type, _type);
1944   }
1945   return type;
1946 }