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, ¶ms, 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, ¶ms, 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