• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/video/video.h>
28 
29 #include <va/va_drmcommon.h>
30 
31 #include "gstvaallocator.h"
32 #include "gstvacaps.h"
33 #include "gstvadisplay_priv.h"
34 #include "gstvavideoformat.h"
35 #include "vasurfaceimage.h"
36 
37 struct _GstVaFilter
38 {
39   GstObject parent;
40 
41   GstVaDisplay *display;
42   VAConfigID config;
43   VAContextID context;
44 
45   /* hardware constraints */
46   VAProcPipelineCaps pipeline_caps;
47 
48   guint32 mem_types;
49   gint min_width;
50   gint max_width;
51   gint min_height;
52   gint max_height;
53 
54   GArray *surface_formats;
55   GArray *image_formats;
56 
57   GArray *available_filters;
58 
59   /* stream information */
60   guint mirror;
61   guint rotation;
62   GstVideoOrientationMethod orientation;
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
gst_va_filter_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)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
gst_va_filter_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)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
gst_va_filter_dispose(GObject * object)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
gst_va_filter_class_init(GstVaFilterClass * klass)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
gst_va_filter_init(GstVaFilter * self)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 *
gst_va_filter_new(GstVaDisplay * display)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
gst_va_filter_is_open(GstVaFilter * self)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
gst_va_filter_ensure_config_attributes(GstVaFilter * self,guint32 * rt_formats_ptr)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   gst_va_display_lock (self->display);
207   status = vaGetConfigAttributes (dpy, VAProfileNone, VAEntrypointVideoProc,
208       attribs, G_N_ELEMENTS (attribs));
209   gst_va_display_unlock (self->display);
210   if (status != VA_STATUS_SUCCESS) {
211     GST_ERROR_OBJECT (self, "vaGetConfigAttributes: %s", vaErrorStr (status));
212     return FALSE;
213   }
214 
215   for (i = 0; i < G_N_ELEMENTS (attribs); i++) {
216     value = attribs[i].value;
217     if (value == VA_ATTRIB_NOT_SUPPORTED)
218       continue;
219     switch (attribs[i].type) {
220       case VAConfigAttribMaxPictureHeight:
221         max_height = value;
222         break;
223       case VAConfigAttribMaxPictureWidth:
224         max_width = value;
225         break;
226       case VAConfigAttribRTFormat:
227         rt_formats = value;
228         break;
229       default:
230         break;
231     }
232   }
233 
234   if (rt_formats_ptr && rt_formats != 0)
235     *rt_formats_ptr = rt_formats;
236   if (max_width > 0 && max_width < G_MAXINT)
237     self->max_width = max_width;
238   if (max_height > 0 && max_height < G_MAXINT)
239     self->max_height = max_height;
240 
241   return TRUE;
242 }
243 
244 /* There are formats that are not handled correctly by driver */
245 static gboolean
format_is_accepted(GstVaFilter * self,GstVideoFormat format)246 format_is_accepted (GstVaFilter * self, GstVideoFormat format)
247 {
248   /* https://github.com/intel/media-driver/issues/690
249    * https://github.com/intel/media-driver/issues/644 */
250   if (!gst_va_display_is_implementation (self->display,
251           GST_VA_IMPLEMENTATION_INTEL_IHD))
252     return TRUE;
253 
254   switch (format) {
255     case GST_VIDEO_FORMAT_ARGB:
256     case GST_VIDEO_FORMAT_xRGB:
257     case GST_VIDEO_FORMAT_ABGR:
258     case GST_VIDEO_FORMAT_xBGR:
259       return FALSE;
260     default:
261       break;
262   }
263 
264   return TRUE;
265 }
266 
267 static gboolean
gst_va_filter_ensure_surface_attributes(GstVaFilter * self)268 gst_va_filter_ensure_surface_attributes (GstVaFilter * self)
269 {
270   GArray *surface_formats;
271   GstVideoFormat format;
272   VASurfaceAttrib *attribs;
273   guint i, attrib_count;
274 
275   attribs =
276       gst_va_get_surface_attribs (self->display, self->config, &attrib_count);
277   if (!attribs)
278     return FALSE;
279   surface_formats = g_array_new (FALSE, FALSE, sizeof (GstVideoFormat));
280 
281   for (i = 0; i < attrib_count; i++) {
282     if (attribs[i].value.type != VAGenericValueTypeInteger)
283       continue;
284     switch (attribs[i].type) {
285       case VASurfaceAttribPixelFormat:
286         format = gst_va_video_format_from_va_fourcc (attribs[i].value.value.i);
287         if (format != GST_VIDEO_FORMAT_UNKNOWN
288             && format_is_accepted (self, format))
289           g_array_append_val (surface_formats, format);
290         break;
291       case VASurfaceAttribMinWidth:
292         self->min_width = MAX (self->min_width, attribs[i].value.value.i);
293         break;
294       case VASurfaceAttribMaxWidth:
295         if (self->max_width > 0)
296           self->max_width = MIN (self->max_width, attribs[i].value.value.i);
297         else
298           self->max_width = attribs[i].value.value.i;
299         break;
300       case VASurfaceAttribMinHeight:
301         self->min_height = MAX (self->min_height, attribs[i].value.value.i);
302         break;
303       case VASurfaceAttribMaxHeight:
304         if (self->max_height > 0)
305           self->max_height = MIN (self->max_height, attribs[i].value.value.i);
306         else
307           self->max_height = attribs[i].value.value.i;
308         break;
309       case VASurfaceAttribMemoryType:
310         self->mem_types = attribs[i].value.value.i;
311         break;
312       default:
313         break;
314     }
315   }
316 
317   if (surface_formats->len == 0)
318     g_clear_pointer (&surface_formats, g_array_unref);
319 
320   self->surface_formats = surface_formats;
321 
322   g_free (attribs);
323 
324   return TRUE;
325 }
326 
327 static gboolean
gst_va_filter_ensure_pipeline_caps(GstVaFilter * self)328 gst_va_filter_ensure_pipeline_caps (GstVaFilter * self)
329 {
330   VADisplay dpy;
331   VAStatus status;
332 
333   dpy = gst_va_display_get_va_dpy (self->display);
334 
335   gst_va_display_lock (self->display);
336   status = vaQueryVideoProcPipelineCaps (dpy, self->context, NULL, 0,
337       &self->pipeline_caps);
338   gst_va_display_unlock (self->display);
339   if (status != VA_STATUS_SUCCESS) {
340     GST_ERROR_OBJECT (self, "vaQueryVideoProcPipelineCaps: %s",
341         vaErrorStr (status));
342     return FALSE;
343   }
344 
345   return TRUE;
346 }
347 
348 /* Not thread-safe API */
349 gboolean
gst_va_filter_open(GstVaFilter * self)350 gst_va_filter_open (GstVaFilter * self)
351 {
352   VAConfigAttrib attrib = {
353     .type = VAConfigAttribRTFormat,
354   };
355   VADisplay dpy;
356   VAStatus status;
357 
358   g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
359 
360   if (gst_va_filter_is_open (self))
361     return TRUE;
362 
363   if (!gst_va_filter_ensure_config_attributes (self, &attrib.value))
364     return FALSE;
365 
366   if (!gst_va_filter_ensure_pipeline_caps (self))
367     return FALSE;
368 
369   self->image_formats = gst_va_display_get_image_formats (self->display);
370   if (!self->image_formats)
371     return FALSE;
372 
373   dpy = gst_va_display_get_va_dpy (self->display);
374 
375   gst_va_display_lock (self->display);
376   status = vaCreateConfig (dpy, VAProfileNone, VAEntrypointVideoProc, &attrib,
377       1, &self->config);
378   gst_va_display_unlock (self->display);
379   if (status != VA_STATUS_SUCCESS) {
380     GST_ERROR_OBJECT (self, "vaCreateConfig: %s", vaErrorStr (status));
381     return FALSE;
382   }
383 
384   if (!gst_va_filter_ensure_surface_attributes (self))
385     goto bail;
386 
387   gst_va_display_lock (self->display);
388   status = vaCreateContext (dpy, self->config, 0, 0, 0, NULL, 0,
389       &self->context);
390   gst_va_display_unlock (self->display);
391   if (status != VA_STATUS_SUCCESS) {
392     GST_ERROR_OBJECT (self, "vaCreateContext: %s", vaErrorStr (status));
393     goto bail;
394   }
395 
396   return TRUE;
397 
398 bail:
399   {
400     gst_va_display_lock (self->display);
401     status = vaDestroyConfig (dpy, self->config);
402     gst_va_display_unlock (self->display);
403 
404     return FALSE;
405   }
406 }
407 
408 /* Not thread-safe API */
409 gboolean
gst_va_filter_close(GstVaFilter * self)410 gst_va_filter_close (GstVaFilter * self)
411 {
412   VADisplay dpy;
413   VAStatus status;
414 
415   g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
416 
417   if (!gst_va_filter_is_open (self))
418     return TRUE;
419 
420   dpy = gst_va_display_get_va_dpy (self->display);
421 
422   if (self->context != VA_INVALID_ID) {
423     gst_va_display_lock (self->display);
424     status = vaDestroyContext (dpy, self->context);
425     gst_va_display_unlock (self->display);
426     if (status != VA_STATUS_SUCCESS)
427       GST_ERROR_OBJECT (self, "vaDestroyContext: %s", vaErrorStr (status));
428   }
429 
430   gst_va_display_lock (self->display);
431   status = vaDestroyConfig (dpy, self->config);
432   gst_va_display_unlock (self->display);
433   if (status != VA_STATUS_SUCCESS) {
434     GST_ERROR_OBJECT (self, "vaDestroyConfig: %s", vaErrorStr (status));
435     return FALSE;
436   }
437 
438   g_clear_pointer (&self->available_filters, g_array_unref);
439   g_clear_pointer (&self->filters, g_array_unref);
440 
441   gst_va_filter_init (self);
442 
443   return TRUE;
444 }
445 
446 /* *INDENT-OFF* */
447 static const struct VaFilterCapMap {
448   VAProcFilterType type;
449   guint count;
450 } filter_cap_map[] = {
451   { VAProcFilterNoiseReduction, 1 },
452   { VAProcFilterDeinterlacing, VAProcDeinterlacingCount },
453   { VAProcFilterSharpening, 1 },
454   { VAProcFilterColorBalance, VAProcColorBalanceCount },
455   { VAProcFilterSkinToneEnhancement, 1 },
456   { VAProcFilterTotalColorCorrection, VAProcTotalColorCorrectionCount },
457   { VAProcFilterHVSNoiseReduction, 0 },
458   { VAProcFilterHighDynamicRangeToneMapping, 1 },
459 };
460 /* *INDENT-ON* */
461 
462 static const struct VaFilterCapMap *
gst_va_filter_get_filter_cap(VAProcFilterType type)463 gst_va_filter_get_filter_cap (VAProcFilterType type)
464 {
465   guint i;
466 
467   for (i = 0; i < G_N_ELEMENTS (filter_cap_map); i++) {
468     if (filter_cap_map[i].type == type)
469       return &filter_cap_map[i];
470   }
471 
472   return NULL;
473 }
474 
475 static guint
gst_va_filter_get_filter_cap_count(VAProcFilterType type)476 gst_va_filter_get_filter_cap_count (VAProcFilterType type)
477 {
478   const struct VaFilterCapMap *map = gst_va_filter_get_filter_cap (type);
479   return map ? map->count : 0;
480 }
481 
482 struct VaFilter
483 {
484   VAProcFilterType type;
485   guint num_caps;
486   union
487   {
488     VAProcFilterCap simple;
489     VAProcFilterCapDeinterlacing deint[VAProcDeinterlacingCount];
490     VAProcFilterCapColorBalance cb[VAProcColorBalanceCount];
491     VAProcFilterCapTotalColorCorrection cc[VAProcTotalColorCorrectionCount];
492     VAProcFilterCapHighDynamicRange hdr;
493   } caps;
494 };
495 
496 static gboolean
gst_va_filter_ensure_filters(GstVaFilter * self)497 gst_va_filter_ensure_filters (GstVaFilter * self)
498 {
499   GArray *filters;
500   VADisplay dpy;
501   VAProcFilterType *filter_types;
502   VAStatus status;
503   guint i, num = VAProcFilterCount;
504   gboolean ret = FALSE;
505 
506   GST_OBJECT_LOCK (self);
507   if (self->available_filters) {
508     GST_OBJECT_UNLOCK (self);
509     return TRUE;
510   }
511   GST_OBJECT_UNLOCK (self);
512 
513   filter_types = g_malloc_n (num, sizeof (*filter_types));
514 
515   dpy = gst_va_display_get_va_dpy (self->display);
516 
517   gst_va_display_lock (self->display);
518   status = vaQueryVideoProcFilters (dpy, self->context, filter_types, &num);
519   gst_va_display_unlock (self->display);
520   if (status == VA_STATUS_ERROR_MAX_NUM_EXCEEDED) {
521     filter_types = g_try_realloc_n (filter_types, num, sizeof (*filter_types));
522     gst_va_display_lock (self->display);
523     status = vaQueryVideoProcFilters (dpy, self->context, filter_types, &num);
524     gst_va_display_unlock (self->display);
525   }
526   if (status != VA_STATUS_SUCCESS) {
527     GST_ERROR_OBJECT (self, "vaQueryVideoProcFilters: %s", vaErrorStr (status));
528     goto bail;
529   }
530 
531   if (num == 0)
532     goto bail;
533 
534   filters = g_array_sized_new (FALSE, FALSE, sizeof (struct VaFilter), num);
535 
536   for (i = 0; i < num; i++) {
537     guint num_caps = gst_va_filter_get_filter_cap_count (filter_types[i]);
538     struct VaFilter filter = { filter_types[i], num_caps, {{{0,}}} };
539 
540     if (num_caps > 0) {
541       gst_va_display_lock (self->display);
542       status = vaQueryVideoProcFilterCaps (dpy, self->context, filter.type,
543           &filter.caps, &filter.num_caps);
544       gst_va_display_unlock (self->display);
545       if (status != VA_STATUS_SUCCESS) {
546         GST_WARNING_OBJECT (self, "vaQueryVideoProcFiltersCaps: %s",
547             vaErrorStr (status));
548         continue;
549       }
550     }
551 
552     g_array_append_val (filters, filter);
553   }
554 
555   GST_OBJECT_LOCK (self);
556   g_clear_pointer (&self->available_filters, g_array_unref);
557   self->available_filters = filters;
558   GST_OBJECT_UNLOCK (self);
559 
560   ret = TRUE;
561 
562 bail:
563   g_free (filter_types);
564 
565   return ret;
566 }
567 
568 /* *INDENT-OFF* */
569 static const struct _CBDesc {
570   const char *name;
571   const char *nick;
572   const char *blurb;
573   guint prop_id;
574 } cb_desc[VAProcColorBalanceCount] = {
575   [VAProcColorBalanceHue] =
576       { "hue", "Hue", "Color hue value", GST_VA_FILTER_PROP_HUE },
577   [VAProcColorBalanceSaturation] =
578       { "saturation", "Saturation", "Color saturation value",
579         GST_VA_FILTER_PROP_SATURATION },
580   [VAProcColorBalanceBrightness] =
581       { "brightness", "Brightness", "Color brightness value",
582         GST_VA_FILTER_PROP_BRIGHTNESS },
583   [VAProcColorBalanceContrast] =
584       { "contrast", "Contrast", "Color contrast value",
585         GST_VA_FILTER_PROP_CONTRAST },
586   [VAProcColorBalanceAutoSaturation] =
587       { "auto-saturation",   "Auto-Saturation", "Enable auto saturation",
588         GST_VA_FILTER_PROP_AUTO_SATURATION },
589   [VAProcColorBalanceAutoBrightness] =
590       { "auto-brightness", "Auto-Brightness", "Enable auto brightness",
591         GST_VA_FILTER_PROP_AUTO_BRIGHTNESS    },
592   [VAProcColorBalanceAutoContrast] =
593       { "auto-contrast", "Auto-Contrast", "Enable auto contrast",
594         GST_VA_FILTER_PROP_AUTO_CONTRAST },
595 };
596 /* *INDENT-ON* */
597 
598 gboolean
gst_va_filter_install_properties(GstVaFilter * self,GObjectClass * klass)599 gst_va_filter_install_properties (GstVaFilter * self, GObjectClass * klass)
600 {
601   guint i;
602   const GParamFlags common_flags = G_PARAM_READWRITE
603       | GST_PARAM_CONDITIONALLY_AVAILABLE | G_PARAM_STATIC_STRINGS
604       | GST_PARAM_MUTABLE_PLAYING | GST_PARAM_CONTROLLABLE;
605 
606   g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
607 
608   if (!gst_va_filter_is_open (self))
609     return FALSE;
610 
611   if (!gst_va_filter_ensure_filters (self))
612     return FALSE;
613 
614   for (i = 0; i < self->available_filters->len; i++) {
615     const struct VaFilter *filter =
616         &g_array_index (self->available_filters, struct VaFilter, i);
617 
618     switch (filter->type) {
619       case VAProcFilterNoiseReduction:{
620         const VAProcFilterCap *caps = &filter->caps.simple;
621 
622         g_object_class_install_property (klass, GST_VA_FILTER_PROP_DENOISE,
623             g_param_spec_float ("denoise", "Noise reduction",
624                 "Noise reduction factor", caps->range.min_value,
625                 caps->range.max_value, caps->range.default_value,
626                 common_flags));
627         break;
628       }
629       case VAProcFilterSharpening:{
630         const VAProcFilterCap *caps = &filter->caps.simple;
631 
632         g_object_class_install_property (klass, GST_VA_FILTER_PROP_SHARPEN,
633             g_param_spec_float ("sharpen", "Sharpening Level",
634                 "Sharpening/blurring filter", caps->range.min_value,
635                 caps->range.max_value, caps->range.default_value,
636                 common_flags));
637         break;
638       }
639       case VAProcFilterSkinToneEnhancement:{
640         const VAProcFilterCap *caps = &filter->caps.simple;
641         GParamSpec *pspec;
642 
643         /* i965 filter */
644         if (filter->num_caps == 0) {
645           pspec = g_param_spec_boolean ("skin-tone", "Skin Tone Enhancenment",
646               "Skin Tone Enhancenment filter", FALSE, common_flags);
647         } else {
648           pspec = g_param_spec_float ("skin-tone", "Skin Tone Enhancenment",
649               "Skin Tone Enhancenment filter", caps->range.min_value,
650               caps->range.max_value, caps->range.default_value, common_flags);
651         }
652 
653         g_object_class_install_property (klass, GST_VA_FILTER_PROP_SKINTONE,
654             pspec);
655         break;
656       }
657       case VAProcFilterColorBalance:{
658         const VAProcFilterCapColorBalance *caps = filter->caps.cb;
659         GParamSpec *pspec;
660         guint j, k;
661 
662         for (j = 0; j < filter->num_caps; j++) {
663           k = caps[j].type;
664           if (caps[j].range.min_value < caps[j].range.max_value) {
665             pspec = g_param_spec_float (cb_desc[k].name, cb_desc[k].nick,
666                 cb_desc[k].blurb, caps[j].range.min_value,
667                 caps[j].range.max_value, caps[j].range.default_value,
668                 common_flags);
669           } else {
670             pspec = g_param_spec_boolean (cb_desc[k].name, cb_desc[k].nick,
671                 cb_desc[k].blurb, FALSE, common_flags);
672           }
673 
674           g_object_class_install_property (klass, cb_desc[k].prop_id, pspec);
675         }
676 
677         break;
678       }
679       default:
680         break;
681     }
682   }
683 
684   if (self->pipeline_caps.mirror_flags != VA_MIRROR_NONE
685       || self->pipeline_caps.rotation_flags != VA_ROTATION_NONE) {
686     g_object_class_install_property (klass, GST_VA_FILTER_PROP_VIDEO_DIR,
687         g_param_spec_enum ("video-direction", "Video Direction",
688             "Video direction: rotation and flipping",
689             GST_TYPE_VIDEO_ORIENTATION_METHOD, GST_VIDEO_ORIENTATION_IDENTITY,
690             common_flags));
691   }
692 
693   /**
694    * GstVaPostProc:disable-passthrough:
695    *
696    * If set to %TRUE the filter will not enable passthrough mode, thus
697    * each frame will be processed. It's useful for cropping, for
698    * example.
699    *
700    * Since: 1.20
701    */
702   g_object_class_install_property (klass,
703       GST_VA_FILTER_PROP_DISABLE_PASSTHROUGH,
704       g_param_spec_boolean ("disable-passthrough", "Disable Passthrough",
705           "Forces passing buffers through the postprocessor", FALSE,
706           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS
707           | GST_PARAM_MUTABLE_READY));
708 
709   /**
710    * GstVaPostProc:add-borders:
711    *
712    * If set to %TRUE the filter will add black borders if necessary to
713    * keep the display aspect ratio.
714    *
715    * Since: 1.20
716    */
717   g_object_class_install_property (klass, GST_VA_FILTER_PROP_ADD_BORDERS,
718       g_param_spec_boolean ("add-borders", "Add Borders",
719           "Add black borders if necessary to keep the display aspect ratio",
720           FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS
721           | GST_PARAM_MUTABLE_PLAYING));
722 
723 
724   return TRUE;
725 }
726 
727 /**
728  * GstVaDeinterlaceMethods:
729  * @GST_VA_DEINTERLACE_BOB: Interpolating missing lines by using the
730  *   adjacent lines.
731  * @GST_VA_DEINTERLACE_WEAVE: Show both fields per frame. (don't use)
732  * @GST_VA_DEINTERLACE_ADAPTIVE: Interpolating missing lines by using
733  *   spatial/temporal references.
734  * @GST_VA_DEINTERLACE_COMPENSATED: Recreating missing lines by using
735  *   motion vector.
736  *
737  * Since: 1.20
738  */
739 /* *INDENT-OFF* */
740 static const GEnumValue di_desc[] = {
741   [GST_VA_DEINTERLACE_BOB] =
742       { VAProcDeinterlacingBob,
743         "Bob: Interpolating missing lines by using the adjacent lines.", "bob" },
744   [GST_VA_DEINTERLACE_WEAVE] =
745       { VAProcDeinterlacingWeave, "Weave: Show both fields per frame. (don't use)",
746         "weave" },
747   [GST_VA_DEINTERLACE_ADAPTIVE] =
748       { VAProcDeinterlacingMotionAdaptive,
749         "Adaptive: Interpolating missing lines by using spatial/temporal references.",
750         "adaptive" },
751   [GST_VA_DEINTERLACE_COMPENSATED] =
752       { VAProcDeinterlacingMotionCompensated,
753         "Compensation: Recreating missing lines by using motion vector.",
754         "compensated" },
755 };
756 /* *INDENT-ON* */
757 
758 static GType
gst_va_deinterlace_methods_get_type(guint num_caps,const VAProcFilterCapDeinterlacing * caps)759 gst_va_deinterlace_methods_get_type (guint num_caps,
760     const VAProcFilterCapDeinterlacing * caps)
761 {
762   guint i, j = 0;
763   static GType deinterlace_methods_type = 0;
764   static GEnumValue methods_types[VAProcDeinterlacingCount];
765 
766   if (deinterlace_methods_type > 0)
767     return deinterlace_methods_type;
768 
769   for (i = 0; i < num_caps; i++) {
770     if (caps[i].type > VAProcDeinterlacingNone
771         && caps[i].type < VAProcDeinterlacingCount)
772       methods_types[j++] = di_desc[caps[i].type];
773   }
774 
775   /* *INDENT-OFF* */
776   methods_types[j] = (GEnumValue) { 0, NULL, NULL };
777   /* *INDENT-ON* */
778 
779   deinterlace_methods_type = g_enum_register_static ("GstVaDeinterlaceMethods",
780       (const GEnumValue *) methods_types);
781 
782   return deinterlace_methods_type;
783 }
784 
785 gboolean
gst_va_filter_install_deinterlace_properties(GstVaFilter * self,GObjectClass * klass)786 gst_va_filter_install_deinterlace_properties (GstVaFilter * self,
787     GObjectClass * klass)
788 {
789   GType type;
790   guint i;
791   const GParamFlags common_flags = G_PARAM_READWRITE
792       | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_PLAYING;
793 
794   g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
795 
796   if (!gst_va_filter_is_open (self))
797     return FALSE;
798 
799   if (!gst_va_filter_ensure_filters (self))
800     return FALSE;
801 
802   for (i = 0; i < self->available_filters->len; i++) {
803     const struct VaFilter *filter =
804         &g_array_index (self->available_filters, struct VaFilter, i);
805 
806     if (filter->type == VAProcFilterDeinterlacing) {
807       guint i, default_method = 0;
808       const VAProcFilterCapDeinterlacing *caps = filter->caps.deint;
809       if (!caps)
810         break;
811 
812       /* use the first method in the list as default */
813       for (i = 0; i < filter->num_caps; i++) {
814         if (caps[i].type > VAProcDeinterlacingNone
815             && caps[i].type < VAProcDeinterlacingCount) {
816           default_method = caps[i].type;
817           break;
818         }
819       }
820 
821       if (default_method == 0)
822         break;
823 
824       type = gst_va_deinterlace_methods_get_type (filter->num_caps, caps);
825       gst_type_mark_as_plugin_api (type, 0);
826 
827       /**
828        * GstVaDeinterlace:method
829        *
830        * Selects the different deinterlacing algorithms that can be used.
831        *
832        * It depends on the driver the number of available algorithms,
833        * and they provide different quality and different processing
834        * consumption.
835        *
836        * Since: 1.20
837        */
838       g_object_class_install_property (klass,
839           GST_VA_FILTER_PROP_DEINTERLACE_METHOD,
840           g_param_spec_enum ("method", "Method", "Deinterlace Method",
841               type, default_method, common_flags));
842 
843       return TRUE;
844     }
845   }
846 
847   return FALSE;
848 }
849 
850 gboolean
gst_va_filter_has_filter(GstVaFilter * self,VAProcFilterType type)851 gst_va_filter_has_filter (GstVaFilter * self, VAProcFilterType type)
852 {
853   guint i;
854 
855   g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
856 
857   if (!gst_va_filter_is_open (self))
858     return FALSE;
859 
860   if (!gst_va_filter_ensure_filters (self))
861     return FALSE;
862 
863   for (i = 0; i < self->available_filters->len; i++) {
864     const struct VaFilter *filter =
865         &g_array_index (self->available_filters, struct VaFilter, i);
866 
867     if (filter->type == type)
868       return TRUE;
869   }
870 
871   return FALSE;
872 }
873 
874 const gpointer
gst_va_filter_get_filter_caps(GstVaFilter * self,VAProcFilterType type,guint * num_caps)875 gst_va_filter_get_filter_caps (GstVaFilter * self, VAProcFilterType type,
876     guint * num_caps)
877 {
878   struct VaFilter *filter = NULL;
879   /* *INDENT-OFF* */
880   static const VAProcFilterCap i965_ste_caps = {
881     .range = {
882       .min_value = 0.0,
883       .max_value = 1.0,
884       .default_value = 0.0,
885       .step = 1.0,
886     },
887   };
888   /* *INDENT-ON* */
889   gpointer ret = NULL;
890   guint i;
891 
892   if (!gst_va_filter_is_open (self))
893     return FALSE;
894 
895   if (!gst_va_filter_ensure_filters (self))
896     return FALSE;
897 
898   GST_OBJECT_LOCK (self);
899   for (i = 0; i < self->available_filters->len; i++) {
900     filter = &g_array_index (self->available_filters, struct VaFilter, i);
901 
902     if (filter->type == type) {
903       if (filter->num_caps > 0)
904         ret = &filter->caps;
905       else if (type == VAProcFilterSkinToneEnhancement && filter->num_caps == 0)
906         ret = (gpointer) & i965_ste_caps;
907       break;
908     }
909   }
910 
911   if (ret && filter && num_caps)
912     *num_caps = filter->num_caps;
913   GST_OBJECT_UNLOCK (self);
914 
915   return ret;
916 }
917 
918 guint32
gst_va_filter_get_mem_types(GstVaFilter * self)919 gst_va_filter_get_mem_types (GstVaFilter * self)
920 {
921   guint32 ret;
922 
923   g_return_val_if_fail (GST_IS_VA_FILTER (self), 0);
924 
925   GST_OBJECT_LOCK (self);
926   ret = self->mem_types;
927   GST_OBJECT_UNLOCK (self);
928 
929   return ret;
930 }
931 
932 GArray *
gst_va_filter_get_surface_formats(GstVaFilter * self)933 gst_va_filter_get_surface_formats (GstVaFilter * self)
934 {
935   GArray *ret;
936 
937   g_return_val_if_fail (GST_IS_VA_FILTER (self), NULL);
938 
939   GST_OBJECT_LOCK (self);
940   ret = self->surface_formats ? g_array_ref (self->surface_formats) : NULL;
941   GST_OBJECT_UNLOCK (self);
942 
943   return ret;
944 }
945 
946 static gboolean
_from_video_orientation_method(GstVideoOrientationMethod orientation,guint * mirror,guint * rotation)947 _from_video_orientation_method (GstVideoOrientationMethod orientation,
948     guint * mirror, guint * rotation)
949 {
950   switch (orientation) {
951     case GST_VIDEO_ORIENTATION_IDENTITY:
952       *mirror = VA_MIRROR_NONE;
953       *rotation = VA_ROTATION_NONE;
954       break;
955     case GST_VIDEO_ORIENTATION_HORIZ:
956       *mirror = VA_MIRROR_HORIZONTAL;
957       *rotation = VA_ROTATION_NONE;
958       break;
959     case GST_VIDEO_ORIENTATION_VERT:
960       *mirror = VA_MIRROR_VERTICAL;
961       *rotation = VA_ROTATION_NONE;
962       break;
963     case GST_VIDEO_ORIENTATION_90R:
964       *mirror = VA_MIRROR_NONE;
965       *rotation = VA_ROTATION_90;
966       break;
967     case GST_VIDEO_ORIENTATION_180:
968       *mirror = VA_MIRROR_NONE;
969       *rotation = VA_ROTATION_180;
970       break;
971     case GST_VIDEO_ORIENTATION_90L:
972       *mirror = VA_MIRROR_NONE;
973       *rotation = VA_ROTATION_270;
974       break;
975     case GST_VIDEO_ORIENTATION_UL_LR:
976       *mirror = VA_MIRROR_HORIZONTAL;
977       *rotation = VA_ROTATION_90;
978       break;
979     case GST_VIDEO_ORIENTATION_UR_LL:
980       *mirror = VA_MIRROR_VERTICAL;
981       *rotation = VA_ROTATION_90;
982       break;
983     default:
984       return FALSE;
985       break;
986   }
987 
988   return TRUE;
989 }
990 
991 gboolean
gst_va_filter_set_orientation(GstVaFilter * self,GstVideoOrientationMethod orientation)992 gst_va_filter_set_orientation (GstVaFilter * self,
993     GstVideoOrientationMethod orientation)
994 {
995   guint32 mirror = VA_MIRROR_NONE, rotation = VA_ROTATION_NONE;
996   guint32 mirror_flags, rotation_flags;
997 
998   if (!gst_va_filter_is_open (self))
999     return FALSE;
1000 
1001   if (!_from_video_orientation_method (orientation, &mirror, &rotation))
1002     return FALSE;
1003 
1004   GST_OBJECT_LOCK (self);
1005   mirror_flags = self->pipeline_caps.mirror_flags;
1006   GST_OBJECT_UNLOCK (self);
1007 
1008   if (mirror != VA_MIRROR_NONE && !(mirror_flags & mirror))
1009     return FALSE;
1010 
1011   GST_OBJECT_LOCK (self);
1012   rotation_flags = self->pipeline_caps.rotation_flags;
1013   GST_OBJECT_UNLOCK (self);
1014 
1015   if (rotation != VA_ROTATION_NONE && !(rotation_flags & (1 << rotation)))
1016     return FALSE;
1017 
1018   GST_OBJECT_LOCK (self);
1019   self->orientation = orientation;
1020   self->mirror = mirror;
1021   self->rotation = rotation;
1022   GST_OBJECT_UNLOCK (self);
1023 
1024   return TRUE;
1025 }
1026 
1027 GstVideoOrientationMethod
gst_va_filter_get_orientation(GstVaFilter * self)1028 gst_va_filter_get_orientation (GstVaFilter * self)
1029 {
1030   GstVideoOrientationMethod ret;
1031 
1032   GST_OBJECT_LOCK (self);
1033   ret = self->orientation;
1034   GST_OBJECT_UNLOCK (self);
1035 
1036   return ret;
1037 }
1038 
1039 void
gst_va_filter_enable_cropping(GstVaFilter * self,gboolean cropping)1040 gst_va_filter_enable_cropping (GstVaFilter * self, gboolean cropping)
1041 {
1042   GST_OBJECT_LOCK (self);
1043   if (cropping != self->crop_enabled)
1044     self->crop_enabled = cropping;
1045   GST_OBJECT_UNLOCK (self);
1046 }
1047 
1048 static inline GstCaps *
_create_base_caps(GstVaFilter * self)1049 _create_base_caps (GstVaFilter * self)
1050 {
1051   return gst_caps_new_simple ("video/x-raw", "width", GST_TYPE_INT_RANGE,
1052       self->min_width, self->max_width, "height", GST_TYPE_INT_RANGE,
1053       self->min_height, self->max_height, NULL);
1054 }
1055 
1056 GstCaps *
gst_va_filter_get_caps(GstVaFilter * self)1057 gst_va_filter_get_caps (GstVaFilter * self)
1058 {
1059   GArray *surface_formats = NULL, *image_formats = NULL;
1060   GstCaps *caps, *base_caps, *feature_caps;
1061   GstCapsFeatures *features;
1062   guint32 mem_types;
1063 
1064   g_return_val_if_fail (GST_IS_VA_FILTER (self), NULL);
1065 
1066   if (!gst_va_filter_is_open (self))
1067     return NULL;
1068 
1069   surface_formats = gst_va_filter_get_surface_formats (self);
1070   if (!surface_formats)
1071     return NULL;
1072 
1073   base_caps = _create_base_caps (self);
1074 
1075   if (!gst_caps_set_format_array (base_caps, surface_formats))
1076     goto fail;
1077 
1078   g_array_unref (surface_formats);
1079 
1080   caps = gst_caps_new_empty ();
1081 
1082   mem_types = gst_va_filter_get_mem_types (self);
1083 
1084   if (mem_types & VA_SURFACE_ATTRIB_MEM_TYPE_VA) {
1085     feature_caps = gst_caps_copy (base_caps);
1086     features = gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_VA);
1087     gst_caps_set_features_simple (feature_caps, features);
1088     caps = gst_caps_merge (caps, feature_caps);
1089   }
1090   if (mem_types & VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME
1091       || mem_types & VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2) {
1092     feature_caps = gst_caps_copy (base_caps);
1093     features = gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_DMABUF);
1094     gst_caps_set_features_simple (feature_caps, features);
1095     caps = gst_caps_merge (caps, feature_caps);
1096   }
1097 
1098   gst_caps_unref (base_caps);
1099 
1100   base_caps = _create_base_caps (self);
1101 
1102   GST_OBJECT_LOCK (self);
1103   image_formats =
1104       self->image_formats ? g_array_ref (self->image_formats) : NULL;
1105   GST_OBJECT_UNLOCK (self);
1106 
1107   if (image_formats) {
1108     if (!gst_caps_set_format_array (base_caps, image_formats))
1109       goto fail;
1110     g_array_unref (image_formats);
1111   }
1112 
1113   return gst_caps_merge (caps, base_caps);
1114 
1115 fail:
1116   {
1117     g_clear_pointer (&surface_formats, g_array_unref);
1118     g_clear_pointer (&image_formats, g_array_unref);
1119     gst_caps_unref (base_caps);
1120     return NULL;
1121   }
1122 }
1123 
1124 /* from va_vpp.h */
1125 /* *INDENT-OFF* */
1126 static const struct ColorPropertiesMap
1127 {
1128   VAProcColorStandardType standard;
1129   guint8 primaries;
1130   guint8 transfer;
1131   guint8 matrix;
1132 } color_properties_map[] = {
1133   { VAProcColorStandardBT601, 5,  6,  5 },
1134   { VAProcColorStandardBT601, 6,  6,  6 },
1135   { VAProcColorStandardBT709, 1,  1,  1 },
1136   { VAProcColorStandardBT470M, 4,  4,  4 },
1137   { VAProcColorStandardBT470BG, 5,  5,  5 },
1138   { VAProcColorStandardSMPTE170M, 6,  6,  6 },
1139   { VAProcColorStandardSMPTE240M, 7,  7,  7 },
1140   { VAProcColorStandardGenericFilm, 8,  1,  1 },
1141   { VAProcColorStandardSRGB, 1, 13,  0 },
1142   /* { VAProcColorStandardSTRGB, ?, ?, ? }, */
1143   { VAProcColorStandardXVYCC601, 1, 11,  5 },
1144   { VAProcColorStandardXVYCC709, 1, 11,  1 },
1145   { VAProcColorStandardBT2020, 9, 14,  9 },
1146 };
1147   /* *INDENT-ON* */
1148 
1149 static guint8
_get_chroma_siting(GstVideoChromaSite chrome_site)1150 _get_chroma_siting (GstVideoChromaSite chrome_site)
1151 {
1152   /* *INDENT-OFF* */
1153   static const struct ChromaSiteMap {
1154     GstVideoChromaSite gst;
1155     guint8 va;
1156   } chroma_site_map[] = {
1157     { GST_VIDEO_CHROMA_SITE_UNKNOWN, VA_CHROMA_SITING_UNKNOWN },
1158     { GST_VIDEO_CHROMA_SITE_NONE, VA_CHROMA_SITING_VERTICAL_CENTER
1159                                   | VA_CHROMA_SITING_HORIZONTAL_CENTER },
1160     { GST_VIDEO_CHROMA_SITE_H_COSITED, VA_CHROMA_SITING_VERTICAL_CENTER
1161                                        | VA_CHROMA_SITING_HORIZONTAL_LEFT },
1162     { GST_VIDEO_CHROMA_SITE_V_COSITED, VA_CHROMA_SITING_VERTICAL_TOP
1163                                        | VA_CHROMA_SITING_VERTICAL_BOTTOM },
1164     { GST_VIDEO_CHROMA_SITE_COSITED, VA_CHROMA_SITING_VERTICAL_CENTER
1165                                      | VA_CHROMA_SITING_HORIZONTAL_LEFT
1166                                      | VA_CHROMA_SITING_VERTICAL_TOP
1167                                      | VA_CHROMA_SITING_VERTICAL_BOTTOM },
1168     { GST_VIDEO_CHROMA_SITE_JPEG, VA_CHROMA_SITING_VERTICAL_CENTER
1169                                   | VA_CHROMA_SITING_HORIZONTAL_CENTER  },
1170     { GST_VIDEO_CHROMA_SITE_MPEG2, VA_CHROMA_SITING_VERTICAL_CENTER
1171                                    | VA_CHROMA_SITING_HORIZONTAL_LEFT },
1172     { GST_VIDEO_CHROMA_SITE_DV, VA_CHROMA_SITING_VERTICAL_TOP
1173                                 | VA_CHROMA_SITING_HORIZONTAL_LEFT },
1174   };
1175   /* *INDENT-ON* */
1176   guint i;
1177 
1178   for (i = 0; i < G_N_ELEMENTS (chroma_site_map); i++) {
1179     if (chrome_site == chroma_site_map[i].gst)
1180       return chroma_site_map[i].va;
1181   }
1182 
1183   return VA_CHROMA_SITING_UNKNOWN;
1184 }
1185 
1186 static guint8
_get_color_range(GstVideoColorRange range)1187 _get_color_range (GstVideoColorRange range)
1188 {
1189   /* *INDENT-OFF* */
1190   static const struct ColorRangeMap {
1191     GstVideoColorRange gst;
1192     guint8 va;
1193   } color_range_map[] = {
1194     { GST_VIDEO_COLOR_RANGE_UNKNOWN, VA_SOURCE_RANGE_UNKNOWN },
1195     { GST_VIDEO_COLOR_RANGE_0_255, VA_SOURCE_RANGE_FULL },
1196     { GST_VIDEO_COLOR_RANGE_16_235, VA_SOURCE_RANGE_REDUCED },
1197   };
1198   /* *INDENT-ON* */
1199   guint i;
1200 
1201   for (i = 0; i < G_N_ELEMENTS (color_range_map); i++) {
1202     if (range == color_range_map[i].gst)
1203       return color_range_map[i].va;
1204   }
1205 
1206   return VA_SOURCE_RANGE_UNKNOWN;
1207 }
1208 
1209 static VAProcColorStandardType
_gst_video_colorimetry_to_va(const GstVideoColorimetry * const colorimetry)1210 _gst_video_colorimetry_to_va (const GstVideoColorimetry * const colorimetry)
1211 {
1212   if (!colorimetry
1213       || colorimetry->primaries == GST_VIDEO_COLOR_PRIMARIES_UNKNOWN)
1214     return VAProcColorStandardNone;
1215 
1216   if (gst_video_colorimetry_matches (colorimetry, GST_VIDEO_COLORIMETRY_BT709))
1217     return VAProcColorStandardBT709;
1218 
1219   /* NOTE: VAProcColorStandardBT2020 in VAAPI is the same as
1220    * GST_VIDEO_COLORIMETRY_BT2020_10 in gstreamer. */
1221   if (gst_video_colorimetry_matches (colorimetry,
1222           GST_VIDEO_COLORIMETRY_BT2020_10) ||
1223       gst_video_colorimetry_matches (colorimetry, GST_VIDEO_COLORIMETRY_BT2020))
1224     return VAProcColorStandardBT2020;
1225 
1226   if (gst_video_colorimetry_matches (colorimetry, GST_VIDEO_COLORIMETRY_BT601))
1227     return VAProcColorStandardBT601;
1228 
1229   if (gst_video_colorimetry_matches (colorimetry,
1230           GST_VIDEO_COLORIMETRY_SMPTE240M))
1231     return VAProcColorStandardSMPTE240M;
1232 
1233   if (gst_video_colorimetry_matches (colorimetry, GST_VIDEO_COLORIMETRY_SRGB))
1234     return VAProcColorStandardSRGB;
1235 
1236   return VAProcColorStandardNone;
1237 }
1238 
1239 static void
_config_color_properties(VAProcColorStandardType * std,VAProcColorProperties * props,const GstVideoInfo * info,VAProcColorStandardType * standards,guint32 num_standards)1240 _config_color_properties (VAProcColorStandardType * std,
1241     VAProcColorProperties * props, const GstVideoInfo * info,
1242     VAProcColorStandardType * standards, guint32 num_standards)
1243 {
1244   GstVideoColorimetry colorimetry = GST_VIDEO_INFO_COLORIMETRY (info);
1245   VAProcColorStandardType best;
1246   gboolean has_explicit;
1247   guint i, j, k;
1248   gint score, bestscore = -1, worstscore;
1249 
1250   best = _gst_video_colorimetry_to_va (&colorimetry);
1251 
1252   has_explicit = FALSE;
1253   for (i = 0; i < num_standards; i++) {
1254     /* Find the exact match standard. */
1255     if (standards[i] != VAProcColorStandardNone && standards[i] == best)
1256       break;
1257 
1258     if (standards[i] == VAProcColorStandardExplicit)
1259       has_explicit = TRUE;
1260   }
1261 
1262   if (i < num_standards) {
1263     *std = best;
1264     goto set_properties;
1265   } else if (has_explicit) {
1266     *std = VAProcColorStandardExplicit;
1267     goto set_properties;
1268   }
1269 
1270   worstscore = 4 * (colorimetry.matrix != GST_VIDEO_COLOR_MATRIX_UNKNOWN
1271       && colorimetry.matrix != GST_VIDEO_COLOR_MATRIX_RGB)
1272       + 2 * (colorimetry.transfer != GST_VIDEO_TRANSFER_UNKNOWN)
1273       + (colorimetry.primaries != GST_VIDEO_COLOR_PRIMARIES_UNKNOWN);
1274 
1275   if (worstscore == 0) {
1276     /* No properties specified, there's not a useful choice. */
1277     *std = VAProcColorStandardNone;
1278     *props = (VAProcColorProperties) {
1279     };
1280 
1281     return;
1282   }
1283 
1284   best = VAProcColorStandardNone;
1285   k = -1;
1286   for (i = 0; i < num_standards; i++) {
1287     for (j = 0; j < G_N_ELEMENTS (color_properties_map); j++) {
1288       if (color_properties_map[j].standard != standards[i])
1289         continue;
1290 
1291       score = 0;
1292       if (colorimetry.matrix != GST_VIDEO_COLOR_MATRIX_UNKNOWN
1293           && colorimetry.matrix != GST_VIDEO_COLOR_MATRIX_RGB)
1294         score += 4 * (colorimetry.matrix != color_properties_map[j].matrix);
1295       if (colorimetry.transfer != GST_VIDEO_TRANSFER_UNKNOWN)
1296         score += 2 * (colorimetry.transfer != color_properties_map[j].transfer);
1297       if (colorimetry.primaries != GST_VIDEO_COLOR_PRIMARIES_UNKNOWN)
1298         score += (colorimetry.primaries != color_properties_map[j].primaries);
1299 
1300       if (score < worstscore && (bestscore == -1 || score < bestscore)) {
1301         bestscore = score;
1302         best = color_properties_map[j].standard;
1303         k = j;
1304       }
1305     }
1306   }
1307 
1308   if (best != VAProcColorStandardNone) {
1309     *std = best;
1310     colorimetry.matrix = color_properties_map[k].matrix;
1311     colorimetry.transfer = color_properties_map[k].transfer;
1312     colorimetry.primaries = color_properties_map[k].primaries;
1313   }
1314 
1315 set_properties:
1316   /* *INDENT-OFF* */
1317   *props = (VAProcColorProperties) {
1318     .chroma_sample_location =
1319         _get_chroma_siting (GST_VIDEO_INFO_CHROMA_SITE (info)),
1320     .color_range = _get_color_range (colorimetry.range),
1321     .colour_primaries =
1322         gst_video_color_primaries_to_iso (colorimetry.primaries),
1323     .transfer_characteristics =
1324         gst_video_transfer_function_to_iso (colorimetry.transfer),
1325     .matrix_coefficients =
1326         gst_video_color_matrix_to_iso (colorimetry.matrix),
1327   };
1328   /* *INDENT-ON* */
1329 }
1330 
1331 gboolean
gst_va_filter_set_video_info(GstVaFilter * self,GstVideoInfo * in_info,GstVideoInfo * out_info)1332 gst_va_filter_set_video_info (GstVaFilter * self, GstVideoInfo * in_info,
1333     GstVideoInfo * out_info)
1334 {
1335   g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
1336   g_return_val_if_fail (out_info && in_info, FALSE);
1337 
1338   if (!gst_va_filter_is_open (self))
1339     return FALSE;
1340 
1341   GST_OBJECT_LOCK (self);
1342   /* *INDENT-OFF* */
1343   self->input_region = (VARectangle) {
1344     .width = GST_VIDEO_INFO_WIDTH (in_info),
1345     .height = GST_VIDEO_INFO_HEIGHT (in_info),
1346   };
1347 
1348   self->output_region = (VARectangle) {
1349     .width = GST_VIDEO_INFO_WIDTH (out_info),
1350     .height = GST_VIDEO_INFO_HEIGHT (out_info),
1351   };
1352   /* *INDENT-ON* */
1353 
1354   _config_color_properties (&self->input_color_standard,
1355       &self->input_color_properties, in_info,
1356       self->pipeline_caps.input_color_standards,
1357       self->pipeline_caps.num_input_color_standards);
1358   _config_color_properties (&self->output_color_standard,
1359       &self->output_color_properties, out_info,
1360       self->pipeline_caps.output_color_standards,
1361       self->pipeline_caps.num_output_color_standards);
1362   GST_OBJECT_UNLOCK (self);
1363 
1364   return TRUE;
1365 }
1366 
1367 static gboolean
_query_pipeline_caps(GstVaFilter * self,GArray * filters,VAProcPipelineCaps * caps)1368 _query_pipeline_caps (GstVaFilter * self, GArray * filters,
1369     VAProcPipelineCaps * caps)
1370 {
1371   VABufferID *va_filters = NULL;
1372   VADisplay dpy;
1373   VAStatus status;
1374   guint32 num_filters = 0;
1375 
1376   GST_OBJECT_LOCK (self);
1377   if (filters) {
1378     num_filters = filters->len;
1379     va_filters = (num_filters > 0) ? (VABufferID *) filters->data : NULL;
1380   }
1381   GST_OBJECT_UNLOCK (self);
1382 
1383   dpy = gst_va_display_get_va_dpy (self->display);
1384 
1385   gst_va_display_lock (self->display);
1386   status = vaQueryVideoProcPipelineCaps (dpy, self->context, va_filters,
1387       num_filters, caps);
1388   gst_va_display_unlock (self->display);
1389 
1390   if (status != VA_STATUS_SUCCESS) {
1391     GST_ERROR_OBJECT (self, "vaQueryVideoProcPipelineCaps: %s",
1392         vaErrorStr (status));
1393     return FALSE;
1394   }
1395 
1396   return TRUE;
1397 }
1398 
1399 gboolean
gst_va_filter_add_deinterlace_buffer(GstVaFilter * self,VAProcDeinterlacingType method,guint32 * forward,guint32 * backward)1400 gst_va_filter_add_deinterlace_buffer (GstVaFilter * self,
1401     VAProcDeinterlacingType method, guint32 * forward, guint32 * backward)
1402 {
1403   GArray *filters = NULL;
1404   VAProcFilterParameterBufferDeinterlacing params = {
1405     .type = VAProcFilterDeinterlacing,
1406     .algorithm = method,
1407   };
1408   VAProcPipelineCaps pipeline_caps = { 0, };
1409   gboolean ret;
1410 
1411   g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
1412 
1413   if (!gst_va_filter_is_open (self))
1414     return FALSE;
1415 
1416   if (!(method != VAProcDeinterlacingNone
1417           && method != VAProcDeinterlacingCount))
1418     return FALSE;
1419 
1420   if (!gst_va_filter_add_filter_buffer (self, &params, sizeof (params), 1))
1421     return FALSE;
1422 
1423   GST_OBJECT_LOCK (self);
1424   if (self->filters)
1425     filters = g_array_ref (self->filters);
1426   GST_OBJECT_UNLOCK (self);
1427   ret = _query_pipeline_caps (self, filters, &pipeline_caps);
1428   if (filters)
1429     g_array_unref (filters);
1430   if (!ret)
1431     return FALSE;
1432 
1433   if (forward)
1434     *forward = pipeline_caps.num_forward_references;
1435   if (backward)
1436     *backward = pipeline_caps.num_backward_references;
1437 
1438   return TRUE;
1439 }
1440 
1441 gboolean
gst_va_filter_add_filter_buffer(GstVaFilter * self,gpointer data,gsize size,guint num)1442 gst_va_filter_add_filter_buffer (GstVaFilter * self, gpointer data, gsize size,
1443     guint num)
1444 {
1445   VABufferID buffer;
1446   VADisplay dpy;
1447   VAStatus status;
1448 
1449   g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
1450   g_return_val_if_fail (data && size > 0, FALSE);
1451 
1452   if (!gst_va_filter_is_open (self))
1453     return FALSE;
1454 
1455   dpy = gst_va_display_get_va_dpy (self->display);
1456   gst_va_display_lock (self->display);
1457   status = vaCreateBuffer (dpy, self->context, VAProcFilterParameterBufferType,
1458       size, num, data, &buffer);
1459   gst_va_display_unlock (self->display);
1460   if (status != VA_STATUS_SUCCESS) {
1461     GST_ERROR_OBJECT (self, "vaCreateBuffer: %s", vaErrorStr (status));
1462     return FALSE;
1463   }
1464 
1465   /* lazy creation */
1466   GST_OBJECT_LOCK (self);
1467   if (!self->filters)
1468     self->filters = g_array_sized_new (FALSE, FALSE, sizeof (VABufferID), 16);
1469 
1470   g_array_append_val (self->filters, buffer);
1471   GST_OBJECT_UNLOCK (self);
1472 
1473   return TRUE;
1474 }
1475 
1476 static gboolean
_destroy_filters_unlocked(GstVaFilter * self)1477 _destroy_filters_unlocked (GstVaFilter * self)
1478 {
1479   VABufferID buffer;
1480   VADisplay dpy;
1481   VAStatus status;
1482   gboolean ret = TRUE;
1483   guint i;
1484 
1485   GST_TRACE_OBJECT (self, "Destroying %u filter buffers", self->filters->len);
1486 
1487   dpy = gst_va_display_get_va_dpy (self->display);
1488 
1489   for (i = 0; i < self->filters->len; i++) {
1490     buffer = g_array_index (self->filters, VABufferID, i);
1491 
1492     gst_va_display_lock (self->display);
1493     status = vaDestroyBuffer (dpy, buffer);
1494     gst_va_display_unlock (self->display);
1495     if (status != VA_STATUS_SUCCESS) {
1496       ret = FALSE;
1497       GST_WARNING_OBJECT (self, "Failed to destroy filter buffer: %s",
1498           vaErrorStr (status));
1499     }
1500   }
1501 
1502   self->filters = g_array_set_size (self->filters, 0);
1503 
1504   return ret;
1505 }
1506 
1507 gboolean
gst_va_filter_drop_filter_buffers(GstVaFilter * self)1508 gst_va_filter_drop_filter_buffers (GstVaFilter * self)
1509 {
1510   gboolean ret = TRUE;
1511 
1512   g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
1513 
1514   GST_OBJECT_LOCK (self);
1515   if (self->filters)
1516     ret = _destroy_filters_unlocked (self);
1517   GST_OBJECT_UNLOCK (self);
1518 
1519   return ret;
1520 }
1521 
1522 static gboolean
_fill_va_sample(GstVaFilter * self,GstVaSample * sample,GstPadDirection direction)1523 _fill_va_sample (GstVaFilter * self, GstVaSample * sample,
1524     GstPadDirection direction)
1525 {
1526   GstVideoCropMeta *crop = NULL;
1527 
1528   if (sample->buffer)
1529     sample->surface = gst_va_buffer_get_surface (sample->buffer);
1530   if (sample->surface == VA_INVALID_ID)
1531     return FALSE;
1532 
1533   /* @FIXME: in gallium vaQuerySurfaceStatus only seems to work with
1534    * encoder's surfaces */
1535   if (!GST_VA_DISPLAY_IS_IMPLEMENTATION (self->display, MESA_GALLIUM)) {
1536     if (!va_check_surface (self->display, sample->surface))
1537       return FALSE;
1538   }
1539 
1540   /* XXX: cropping occurs only in input frames */
1541   if (direction == GST_PAD_SRC) {
1542     GST_OBJECT_LOCK (self);
1543     sample->rect = self->output_region;
1544     sample->rect.x = sample->borders_w / 2;
1545     sample->rect.y = sample->borders_h / 2;
1546     sample->rect.width -= sample->borders_w;
1547     sample->rect.height -= sample->borders_h;
1548     GST_OBJECT_UNLOCK (self);
1549 
1550     return TRUE;
1551   }
1552 
1553   /* if buffer has crop meta, its real size is in video meta */
1554   if (sample->buffer)
1555     crop = gst_buffer_get_video_crop_meta (sample->buffer);
1556 
1557   GST_OBJECT_LOCK (self);
1558   if (crop && self->crop_enabled) {
1559     /* *INDENT-OFF* */
1560     sample->rect = (VARectangle) {
1561       .x = crop->x,
1562       .y = crop->y,
1563       .width = crop->width,
1564       .height = crop->height
1565     };
1566     /* *INDENT-ON* */
1567   } else {
1568     sample->rect = self->input_region;
1569   }
1570   GST_OBJECT_UNLOCK (self);
1571 
1572   return TRUE;
1573 }
1574 
1575 static gboolean
_create_pipeline_buffer(GstVaFilter * self,GstVaSample * src,GstVaSample * dst,GArray * filters,VABufferID * buffer)1576 _create_pipeline_buffer (GstVaFilter * self, GstVaSample * src,
1577     GstVaSample * dst, GArray * filters, VABufferID * buffer)
1578 {
1579   VADisplay dpy;
1580   VAStatus status;
1581   VABufferID *va_filters = NULL;
1582   VAProcPipelineParameterBuffer params;
1583   guint32 num_filters = 0;
1584 
1585   GST_OBJECT_LOCK (self);
1586 
1587   /* *INDENT-OFF* */
1588   if (filters) {
1589     num_filters = filters->len;
1590     va_filters = (num_filters > 0) ? (VABufferID *) filters->data : NULL;
1591   }
1592   params = (VAProcPipelineParameterBuffer) {
1593     .surface = src->surface,
1594     .surface_region = &src->rect,
1595     .surface_color_standard = self->input_color_standard,
1596     .output_region = &dst->rect,
1597     .output_background_color = 0xff000000, /* ARGB black */
1598     .output_color_standard = self->output_color_standard,
1599     .filters = va_filters,
1600     .num_filters = num_filters,
1601     .forward_references = src->forward_references,
1602     .num_forward_references = src->num_forward_references,
1603     .backward_references = src->backward_references,
1604     .num_backward_references = src->num_backward_references,
1605     .rotation_state = self->rotation,
1606     .mirror_state = self->mirror,
1607     .input_surface_flag = src->flags,
1608     .output_surface_flag = dst->flags,
1609     .input_color_properties = self->input_color_properties,
1610     .output_color_properties = self->output_color_properties,
1611   };
1612   /* *INDENT-ON* */
1613 
1614   GST_OBJECT_UNLOCK (self);
1615 
1616   dpy = gst_va_display_get_va_dpy (self->display);
1617   gst_va_display_lock (self->display);
1618   status = vaCreateBuffer (dpy, self->context,
1619       VAProcPipelineParameterBufferType, sizeof (params), 1, &params, buffer);
1620   gst_va_display_unlock (self->display);
1621   if (status != VA_STATUS_SUCCESS) {
1622     GST_ERROR_OBJECT (self, "vaCreateBuffer: %s", vaErrorStr (status));
1623     return FALSE;
1624   }
1625 
1626   GST_TRACE_OBJECT (self, "Created VABufferID %#x with %u filters", *buffer,
1627       num_filters);
1628 
1629   return TRUE;
1630 }
1631 
1632 gboolean
gst_va_filter_process(GstVaFilter * self,GstVaSample * src,GstVaSample * dst)1633 gst_va_filter_process (GstVaFilter * self, GstVaSample * src, GstVaSample * dst)
1634 {
1635   GArray *filters = NULL;
1636   VABufferID buffer;
1637   VADisplay dpy;
1638   VAProcPipelineCaps pipeline_caps = { 0, };
1639   VAStatus status;
1640   gboolean ret = FALSE;
1641 
1642   g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
1643   g_return_val_if_fail (src, FALSE);
1644   g_return_val_if_fail (dst, FALSE);
1645 
1646   if (!gst_va_filter_is_open (self))
1647     return FALSE;
1648 
1649   if (!(_fill_va_sample (self, src, GST_PAD_SINK)
1650           && _fill_va_sample (self, dst, GST_PAD_SRC)))
1651     return FALSE;
1652 
1653   GST_OBJECT_LOCK (self);
1654   if (self->filters)
1655     filters = g_array_ref (self->filters);
1656   GST_OBJECT_UNLOCK (self);
1657 
1658   if (!_query_pipeline_caps (self, filters, &pipeline_caps))
1659     return FALSE;
1660 
1661   if (!_create_pipeline_buffer (self, src, dst, filters, &buffer))
1662     return FALSE;
1663 
1664   if (filters)
1665     g_array_unref (filters);
1666 
1667   dpy = gst_va_display_get_va_dpy (self->display);
1668 
1669   gst_va_display_lock (self->display);
1670   status = vaBeginPicture (dpy, self->context, dst->surface);
1671   gst_va_display_unlock (self->display);
1672   if (status != VA_STATUS_SUCCESS) {
1673     GST_ERROR_OBJECT (self, "vaBeginPicture: %s", vaErrorStr (status));
1674     return FALSE;
1675   }
1676 
1677   gst_va_display_lock (self->display);
1678   status = vaRenderPicture (dpy, self->context, &buffer, 1);
1679   gst_va_display_unlock (self->display);
1680   if (status != VA_STATUS_SUCCESS) {
1681     GST_ERROR_OBJECT (self, "vaRenderPicture: %s", vaErrorStr (status));
1682     goto fail_end_pic;
1683   }
1684 
1685   gst_va_display_lock (self->display);
1686   status = vaEndPicture (dpy, self->context);
1687   gst_va_display_unlock (self->display);
1688   if (status != VA_STATUS_SUCCESS) {
1689     GST_ERROR_OBJECT (self, "vaEndPicture: %s", vaErrorStr (status));
1690     goto bail;
1691   }
1692 
1693   ret = TRUE;
1694 
1695 bail:
1696   gst_va_display_lock (self->display);
1697   status = vaDestroyBuffer (dpy, buffer);
1698   gst_va_display_unlock (self->display);
1699   if (status != VA_STATUS_SUCCESS) {
1700     GST_WARNING_OBJECT (self, "Failed to destroy pipeline buffer: %s",
1701         vaErrorStr (status));
1702   }
1703 
1704   return ret;
1705 
1706 fail_end_pic:
1707   {
1708     gst_va_display_lock (self->display);
1709     status = vaEndPicture (dpy, self->context);
1710     gst_va_display_unlock (self->display);
1711     if (status != VA_STATUS_SUCCESS)
1712       GST_ERROR_OBJECT (self, "vaEndPicture: %s", vaErrorStr (status));
1713     goto bail;
1714   }
1715 }
1716 
1717 /**
1718  * gst_va_buffer_get_surface_flags:
1719  * @buffer: the #GstBuffer to check.
1720  * @info: the #GstVideoInfo with info.
1721  *
1722  * Gets the surface flags, related with interlace given @buffer and
1723  * @info.
1724  *
1725  * Returns: VA surface flags.
1726  */
1727 guint32
gst_va_buffer_get_surface_flags(GstBuffer * buffer,GstVideoInfo * info)1728 gst_va_buffer_get_surface_flags (GstBuffer * buffer, GstVideoInfo * info)
1729 {
1730   guint32 surface_flags = 0;
1731 
1732   if (GST_VIDEO_INFO_INTERLACE_MODE (info) == GST_VIDEO_INTERLACE_MODE_MIXED
1733       || (GST_VIDEO_INFO_INTERLACE_MODE (info) ==
1734           GST_VIDEO_INTERLACE_MODE_INTERLEAVED
1735           && GST_VIDEO_INFO_FIELD_ORDER (info) ==
1736           GST_VIDEO_FIELD_ORDER_UNKNOWN)) {
1737     if (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_FLAG_INTERLACED)) {
1738       if (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_FLAG_TFF)) {
1739         surface_flags = VA_TOP_FIELD_FIRST;
1740       } else {
1741         surface_flags = VA_BOTTOM_FIELD_FIRST;
1742       }
1743     } else {
1744       surface_flags = VA_FRAME_PICTURE;
1745     }
1746   } else if (GST_VIDEO_INFO_FIELD_ORDER (info) ==
1747       GST_VIDEO_FIELD_ORDER_BOTTOM_FIELD_FIRST) {
1748     surface_flags = VA_BOTTOM_FIELD_FIRST;
1749   } else if (GST_VIDEO_INFO_FIELD_ORDER (info) ==
1750       GST_VIDEO_FIELD_ORDER_TOP_FIELD_FIRST) {
1751     surface_flags = VA_TOP_FIELD_FIRST;
1752   }
1753 
1754   return surface_flags;
1755 }
1756 
1757 gboolean
gst_va_filter_has_video_format(GstVaFilter * self,GstVideoFormat format,GstCapsFeatures * feature)1758 gst_va_filter_has_video_format (GstVaFilter * self, GstVideoFormat format,
1759     GstCapsFeatures * feature)
1760 {
1761   guint i;
1762   GstVideoFormat fmt;
1763 
1764   g_return_val_if_fail (GST_IS_VA_FILTER (self), FALSE);
1765   g_return_val_if_fail (format != GST_VIDEO_FORMAT_UNKNOWN, FALSE);
1766   g_return_val_if_fail (GST_IS_CAPS_FEATURES (feature)
1767       && !gst_caps_features_is_any (feature), FALSE);
1768 
1769 
1770   GST_OBJECT_LOCK (self);
1771   for (i = 0; i < self->surface_formats->len; i++) {
1772     fmt = g_array_index (self->surface_formats, GstVideoFormat, i);
1773     if (format == fmt) {
1774       GST_OBJECT_UNLOCK (self);
1775       return TRUE;
1776     }
1777   }
1778   GST_OBJECT_UNLOCK (self);
1779 
1780   if (!gst_caps_features_is_equal (feature,
1781           GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY))
1782     return FALSE;
1783 
1784   GST_OBJECT_LOCK (self);
1785   for (i = 0; i < self->image_formats->len; i++) {
1786     fmt = g_array_index (self->image_formats, GstVideoFormat, i);
1787     if (format == fmt) {
1788       GST_OBJECT_UNLOCK (self);
1789       return TRUE;
1790     }
1791   }
1792   GST_OBJECT_UNLOCK (self);
1793 
1794   return FALSE;
1795 }
1796