vapostproc: Don't add video alignment option in buffer pool.
[platform/upstream/gstreamer.git] / sys / va / gstvavpp.c
1 /* GStreamer
2  * Copyright (C) 2020 Igalia, S.L.
3  *     Author: Víctor Jáquez <vjaquez@igalia.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the0
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 /**
22  * SECTION:element-vapostproc
23  * @title: vapostproc
24  * @short_description: A VA-API base video postprocessing filter
25  *
26  * vapostproc applies different video filters to VA surfaces. These
27  * filters vary depending on the installed and chosen
28  * [VA-API](https://01.org/linuxmedia/vaapi) driver, but usually
29  * resizing and color conversion are available.
30  *
31  * The generated surfaces can be mapped onto main memory as video
32  * frames.
33  *
34  * Use gst-inspect-1.0 to introspect the available capabilities of the
35  * driver's post-processor entry point.
36  *
37  * ## Example launch line
38  * ```
39  * gst-launch-1.0 videotestsrc ! "video/x-raw,format=(string)NV12" ! vapostproc ! autovideosink
40  * ```
41  *
42  * Cropping is supported via buffers' crop meta. It's only done if the
43  * postproccessor is not in passthrough mode or if downstream doesn't
44  * support the crop meta API.
45  *
46  * ### Cropping example
47  * ```
48  * gst-launch-1.0 videotestsrc ! "video/x-raw,format=(string)NV12" ! videocrop bottom=50 left=100 ! vapostproc ! autovideosink
49  * ```
50  *
51  * If the VA driver support color balance filter, with controls such
52  * as hue, brightness, contrast, etc., those controls are exposed both
53  * as element properties and through the #GstColorBalance interface.
54  *
55  * Since: 1.20
56  *
57  */
58
59 /* ToDo:
60  *
61  * + deinterlacing
62  * + HDR tone mapping
63  * + colorimetry
64  * + cropping
65  */
66
67 #ifdef HAVE_CONFIG_H
68 #include "config.h"
69 #endif
70
71 #include "gstvavpp.h"
72
73 #include <gst/base/gstbasetransform.h>
74 #include <gst/video/video.h>
75
76 #include <va/va_drmcommon.h>
77
78 #include "gstvaallocator.h"
79 #include "gstvacaps.h"
80 #include "gstvadisplay_priv.h"
81 #include "gstvafilter.h"
82 #include "gstvapool.h"
83 #include "gstvautils.h"
84
85 GST_DEBUG_CATEGORY_STATIC (gst_va_vpp_debug);
86 #define GST_CAT_DEFAULT gst_va_vpp_debug
87
88 #define GST_VA_VPP(obj)           ((GstVaVpp *) obj)
89 #define GST_VA_VPP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_FROM_INSTANCE (obj), GstVaVppClass))
90 #define GST_VA_VPP_CLASS(klass)    ((GstVaVppClass *) klass)
91
92 #define SWAP(a, b) do { const __typeof__ (a) t = a; a = b; b = t; } while (0)
93
94 typedef struct _GstVaVpp GstVaVpp;
95 typedef struct _GstVaVppClass GstVaVppClass;
96
97 struct _GstVaVppClass
98 {
99   /* GstVideoFilter overlaps functionality */
100   GstBaseTransformClass parent_class;
101
102   gchar *render_device_path;
103 };
104
105 struct _GstVaVpp
106 {
107   GstBaseTransform parent;
108
109   GstVaDisplay *display;
110   GstVaFilter *filter;
111
112   GstCaps *incaps;
113   GstCaps *outcaps;
114   GstCaps *alloccaps;
115   GstVideoInfo in_info;
116   GstVideoInfo out_info;
117   gboolean negotiated;
118
119   GstBufferPool *sinkpad_pool;
120   GstVideoInfo sinkpad_info;
121
122   GstBufferPool *other_pool;
123   GstVideoInfo srcpad_info;
124
125   gboolean rebuild_filters;
126   gboolean forward_crop;
127   guint op_flags;
128
129   /* filters */
130   float denoise;
131   float sharpen;
132   float skintone;
133   float brightness;
134   float contrast;
135   float hue;
136   float saturation;
137   gboolean auto_contrast;
138   gboolean auto_brightness;
139   gboolean auto_saturation;
140   GstVideoOrientationMethod direction;
141   GstVideoOrientationMethod prev_direction;
142   GstVideoOrientationMethod tag_direction;
143
144   GList *channels;
145 };
146
147 static GstElementClass *parent_class = NULL;
148
149 struct CData
150 {
151   gchar *render_device_path;
152   gchar *description;
153 };
154
155 /* convertions that disable passthrough */
156 enum
157 {
158   VPP_CONVERT_SIZE = 1 << 0,
159   VPP_CONVERT_FORMAT = 1 << 1,
160   VPP_CONVERT_FILTERS = 1 << 2,
161   VPP_CONVERT_DIRECTION = 1 << 3,
162   VPP_CONVERT_FEATURE = 1 << 4,
163   VPP_CONVERT_CROP = 1 << 5,
164   VPP_CONVERT_DUMMY = 1 << 6,
165 };
166
167 extern GRecMutex GST_VA_SHARED_LOCK;
168
169 /* *INDENT-OFF* */
170 static const gchar *caps_str = GST_VIDEO_CAPS_MAKE_WITH_FEATURES ("memory:VAMemory",
171             "{ NV12, I420, YV12, YUY2, RGBA, BGRA, P010_10LE, ARGB, ABGR }") " ;"
172             GST_VIDEO_CAPS_MAKE ("{ VUYA, GRAY8, NV12, NV21, YUY2, UYVY, YV12, "
173             "I420, P010_10LE, RGBA, BGRA, ARGB, ABGR  }");
174 /* *INDENT-ON* */
175
176 #define META_TAG_COLORSPACE meta_tag_colorspace_quark
177 static GQuark meta_tag_colorspace_quark;
178 #define META_TAG_SIZE meta_tag_size_quark
179 static GQuark meta_tag_size_quark;
180 #define META_TAG_ORIENTATION meta_tag_orientation_quark
181 static GQuark meta_tag_orientation_quark;
182 #define META_TAG_VIDEO meta_tag_video_quark
183 static GQuark meta_tag_video_quark;
184
185 static void gst_va_vpp_colorbalance_init (gpointer iface, gpointer data);
186 static void gst_va_vpp_rebuild_filters (GstVaVpp * self);
187
188 static void
189 gst_va_vpp_dispose (GObject * object)
190 {
191   GstVaVpp *self = GST_VA_VPP (object);
192
193   if (self->channels)
194     g_list_free_full (g_steal_pointer (&self->channels), g_object_unref);
195
196   if (self->sinkpad_pool) {
197     gst_buffer_pool_set_active (self->sinkpad_pool, FALSE);
198     gst_clear_object (&self->sinkpad_pool);
199   }
200
201   if (self->other_pool) {
202     gst_buffer_pool_set_active (self->other_pool, FALSE);
203     gst_clear_object (&self->other_pool);
204   }
205
206   gst_clear_caps (&self->incaps);
207   gst_clear_caps (&self->outcaps);
208   gst_clear_caps (&self->alloccaps);
209
210   gst_clear_object (&self->filter);
211   gst_clear_object (&self->display);
212
213   G_OBJECT_CLASS (parent_class)->dispose (object);
214 }
215
216 static void
217 gst_va_vpp_update_passthrough (GstVaVpp * self, gboolean reconf)
218 {
219   GstBaseTransform *trans = GST_BASE_TRANSFORM (self);
220   gboolean old, new;
221
222   old = gst_base_transform_is_passthrough (trans);
223
224   GST_OBJECT_LOCK (self);
225   new = (self->op_flags == 0);
226   GST_OBJECT_UNLOCK (self);
227
228   if (old != new) {
229     GST_INFO_OBJECT (self, "%s passthrough", new ? "enabling" : "disabling");
230     if (reconf)
231       gst_base_transform_reconfigure_src (trans);
232     gst_base_transform_set_passthrough (trans, new);
233   }
234 }
235
236 static void
237 _update_properties_unlocked (GstVaVpp * self)
238 {
239   if (!self->filter)
240     return;
241
242   if ((self->direction != GST_VIDEO_ORIENTATION_AUTO
243           && self->direction != self->prev_direction)
244       || (self->direction == GST_VIDEO_ORIENTATION_AUTO
245           && self->tag_direction != self->prev_direction)) {
246
247     GstVideoOrientationMethod direction =
248         (self->direction == GST_VIDEO_ORIENTATION_AUTO) ?
249         self->tag_direction : self->direction;
250
251     if (!gst_va_filter_set_orientation (self->filter, direction)) {
252       if (self->direction == GST_VIDEO_ORIENTATION_AUTO)
253         self->tag_direction = self->prev_direction;
254       else
255         self->direction = self->prev_direction;
256
257       self->op_flags &= ~VPP_CONVERT_DIRECTION;
258
259       /* FIXME: unlocked bus warning message */
260       GST_WARNING_OBJECT (self,
261           "Driver cannot set resquested orientation. Setting it back.");
262     } else {
263       self->prev_direction = direction;
264
265       self->op_flags |= VPP_CONVERT_DIRECTION;
266
267       gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (self));
268     }
269   } else {
270     self->op_flags &= ~VPP_CONVERT_DIRECTION;
271   }
272 }
273
274 static void
275 gst_va_vpp_set_property (GObject * object, guint prop_id,
276     const GValue * value, GParamSpec * pspec)
277 {
278   GstVaVpp *self = GST_VA_VPP (object);
279
280   GST_OBJECT_LOCK (object);
281   switch (prop_id) {
282     case GST_VA_FILTER_PROP_DENOISE:
283       self->denoise = g_value_get_float (value);
284       g_atomic_int_set (&self->rebuild_filters, TRUE);
285       break;
286     case GST_VA_FILTER_PROP_SHARPEN:
287       self->sharpen = g_value_get_float (value);
288       g_atomic_int_set (&self->rebuild_filters, TRUE);
289       break;
290     case GST_VA_FILTER_PROP_SKINTONE:
291       if (G_VALUE_TYPE (value) == G_TYPE_BOOLEAN)
292         self->skintone = (float) g_value_get_boolean (value);
293       else
294         self->skintone = g_value_get_float (value);
295       g_atomic_int_set (&self->rebuild_filters, TRUE);
296       break;
297     case GST_VA_FILTER_PROP_VIDEO_DIR:{
298       GstVideoOrientationMethod direction = g_value_get_enum (value);
299       self->prev_direction = (direction == GST_VIDEO_ORIENTATION_AUTO) ?
300           self->tag_direction : self->direction;
301       self->direction = direction;
302       break;
303     }
304     case GST_VA_FILTER_PROP_HUE:
305       self->hue = g_value_get_float (value);
306       g_atomic_int_set (&self->rebuild_filters, TRUE);
307       break;
308     case GST_VA_FILTER_PROP_SATURATION:
309       self->saturation = g_value_get_float (value);
310       g_atomic_int_set (&self->rebuild_filters, TRUE);
311       break;
312     case GST_VA_FILTER_PROP_BRIGHTNESS:
313       self->brightness = g_value_get_float (value);
314       g_atomic_int_set (&self->rebuild_filters, TRUE);
315       break;
316     case GST_VA_FILTER_PROP_CONTRAST:
317       self->contrast = g_value_get_float (value);
318       g_atomic_int_set (&self->rebuild_filters, TRUE);
319       break;
320     case GST_VA_FILTER_PROP_AUTO_SATURATION:
321       self->auto_saturation = g_value_get_boolean (value);
322       g_atomic_int_set (&self->rebuild_filters, TRUE);
323       break;
324     case GST_VA_FILTER_PROP_AUTO_BRIGHTNESS:
325       self->auto_brightness = g_value_get_boolean (value);
326       g_atomic_int_set (&self->rebuild_filters, TRUE);
327       break;
328     case GST_VA_FILTER_PROP_AUTO_CONTRAST:
329       self->auto_contrast = g_value_get_boolean (value);
330       g_atomic_int_set (&self->rebuild_filters, TRUE);
331       break;
332     case GST_VA_FILTER_PROP_DISABLE_PASSTHROUGH:{
333       gboolean disable_passthrough = g_value_get_boolean (value);
334       if (disable_passthrough)
335         self->op_flags |= VPP_CONVERT_DUMMY;
336       else
337         self->op_flags &= ~VPP_CONVERT_DUMMY;
338       break;
339     }
340     default:
341       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
342       break;
343   }
344
345   _update_properties_unlocked (self);
346   GST_OBJECT_UNLOCK (object);
347
348   /* no reconfig here because it's done in
349    * _update_properties_unlocked() */
350   gst_va_vpp_update_passthrough (self, FALSE);
351 }
352
353 static void
354 gst_va_vpp_get_property (GObject * object, guint prop_id, GValue * value,
355     GParamSpec * pspec)
356 {
357   GstVaVpp *self = GST_VA_VPP (object);
358
359   GST_OBJECT_LOCK (object);
360   switch (prop_id) {
361     case GST_VA_FILTER_PROP_DENOISE:
362       g_value_set_float (value, self->denoise);
363       break;
364     case GST_VA_FILTER_PROP_SHARPEN:
365       g_value_set_float (value, self->sharpen);
366       break;
367     case GST_VA_FILTER_PROP_SKINTONE:
368       if (G_VALUE_TYPE (value) == G_TYPE_BOOLEAN)
369         g_value_set_boolean (value, self->skintone > 0);
370       else
371         g_value_set_float (value, self->skintone);
372       break;
373     case GST_VA_FILTER_PROP_VIDEO_DIR:
374       g_value_set_enum (value, self->direction);
375       break;
376     case GST_VA_FILTER_PROP_HUE:
377       g_value_set_float (value, self->hue);
378       break;
379     case GST_VA_FILTER_PROP_SATURATION:
380       g_value_set_float (value, self->saturation);
381       break;
382     case GST_VA_FILTER_PROP_BRIGHTNESS:
383       g_value_set_float (value, self->brightness);
384       break;
385     case GST_VA_FILTER_PROP_CONTRAST:
386       g_value_set_float (value, self->contrast);
387       break;
388     case GST_VA_FILTER_PROP_AUTO_SATURATION:
389       g_value_set_boolean (value, self->auto_saturation);
390       break;
391     case GST_VA_FILTER_PROP_AUTO_BRIGHTNESS:
392       g_value_set_boolean (value, self->auto_brightness);
393       break;
394     case GST_VA_FILTER_PROP_AUTO_CONTRAST:
395       g_value_set_boolean (value, self->auto_contrast);
396       break;
397     case GST_VA_FILTER_PROP_DISABLE_PASSTHROUGH:
398       g_value_set_boolean (value, (self->op_flags & VPP_CONVERT_DUMMY));
399       break;
400     default:
401       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
402       break;
403   }
404   GST_OBJECT_UNLOCK (object);
405 }
406
407 static GstStateChangeReturn
408 gst_va_vpp_change_state (GstElement * element, GstStateChange transition)
409 {
410   GstVaVpp *self = GST_VA_VPP (element);
411   GstVaVppClass *klass = GST_VA_VPP_GET_CLASS (element);
412   GstStateChangeReturn ret;
413
414   switch (transition) {
415     case GST_STATE_CHANGE_NULL_TO_READY:
416       if (!gst_va_ensure_element_data (element, klass->render_device_path,
417               &self->display))
418         goto open_failed;
419       if (!self->filter)
420         self->filter = gst_va_filter_new (self->display);
421       if (!gst_va_filter_open (self->filter))
422         goto open_failed;
423       _update_properties_unlocked (self);
424       gst_va_vpp_rebuild_filters (self);
425       gst_va_vpp_update_passthrough (self, FALSE);
426       break;
427     default:
428       break;
429   }
430
431   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
432
433   switch (transition) {
434     case GST_STATE_CHANGE_PAUSED_TO_READY:
435       gst_va_filter_close (self->filter);
436       break;
437     case GST_STATE_CHANGE_READY_TO_NULL:
438       gst_clear_object (&self->filter);
439       gst_clear_object (&self->display);
440       break;
441     default:
442       break;
443   }
444
445   return ret;
446
447   /* Errors */
448 open_failed:
449   {
450     GST_ELEMENT_ERROR (self, LIBRARY, INIT, (NULL), ("Failed to open VPP"));
451     return GST_STATE_CHANGE_FAILURE;
452   }
453 }
454
455 static void
456 gst_va_vpp_set_context (GstElement * element, GstContext * context)
457 {
458   GstVaDisplay *old_display, *new_display;
459   GstVaVpp *self = GST_VA_VPP (element);
460   GstVaVppClass *klass = GST_VA_VPP_GET_CLASS (self);
461   gboolean ret;
462
463   old_display = self->display ? gst_object_ref (self->display) : NULL;
464   ret = gst_va_handle_set_context (element, context, klass->render_device_path,
465       &self->display);
466   new_display = self->display ? gst_object_ref (self->display) : NULL;
467
468   if (!ret
469       || (old_display && new_display && old_display != new_display
470           && self->filter)) {
471     GST_ELEMENT_WARNING (element, RESOURCE, BUSY,
472         ("Can't replace VA display while operating"), (NULL));
473   }
474
475   gst_clear_object (&old_display);
476   gst_clear_object (&new_display);
477
478   GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
479 }
480
481 static GstAllocator *
482 _create_allocator (GstVaVpp * self, GstCaps * caps)
483 {
484   GstAllocator *allocator = NULL;
485
486   if (gst_caps_is_dmabuf (caps)) {
487     allocator = gst_va_dmabuf_allocator_new (self->display);
488   } else {
489     GArray *surface_formats = gst_va_filter_get_surface_formats (self->filter);
490     allocator = gst_va_allocator_new (self->display, surface_formats);
491   }
492
493   return allocator;
494 }
495
496 static GstBufferPool *
497 _create_sinkpad_bufferpool (GstCaps * caps, guint size, guint min_buffers,
498     guint max_buffers, guint usage_hint, GstAllocator * allocator,
499     GstAllocationParams * alloc_params)
500 {
501   GstBufferPool *pool;
502   GstStructure *config;
503
504   pool = gst_va_pool_new ();
505
506   config = gst_buffer_pool_get_config (pool);
507   gst_buffer_pool_config_set_params (config, caps, size, min_buffers,
508       max_buffers);
509   gst_buffer_pool_config_set_va_allocation_params (config, usage_hint);
510   gst_buffer_pool_config_set_allocator (config, allocator, alloc_params);
511   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
512
513   if (!gst_buffer_pool_set_config (pool, config))
514     gst_clear_object (&pool);
515
516   return pool;
517 }
518
519 /* Answer the allocation query downstream. */
520 static gboolean
521 gst_va_vpp_propose_allocation (GstBaseTransform * trans,
522     GstQuery * decide_query, GstQuery * query)
523 {
524   GstVaVpp *self = GST_VA_VPP (trans);
525   GstVideoInfo info;
526   GstBufferPool *pool;
527   GstCaps *caps;
528   guint size;
529
530   gst_clear_caps (&self->alloccaps);
531
532   if (!GST_BASE_TRANSFORM_CLASS (parent_class)->propose_allocation (trans,
533           decide_query, query)) {
534     self->forward_crop = FALSE;
535     return FALSE;
536   }
537
538   self->forward_crop =
539       (gst_query_find_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE,
540           NULL)
541       && gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL));
542
543   gst_query_parse_allocation (query, &caps, NULL);
544   if (caps == NULL)
545     return FALSE;
546   if (!gst_video_info_from_caps (&info, caps))
547     return FALSE;
548
549   self->alloccaps = gst_caps_ref (caps);
550
551   /* passthrough, we're done */
552   if (decide_query == NULL)
553     return TRUE;
554
555   size = GST_VIDEO_INFO_SIZE (&info);
556
557   if (gst_query_get_n_allocation_pools (query) == 0) {
558     GstAllocator *allocator = NULL;
559     GstAllocationParams params = { 0, };
560     gboolean update_allocator = FALSE;
561     guint usage_hint = VA_SURFACE_ATTRIB_USAGE_HINT_GENERIC;    /* it migth be used by a va decoder */
562
563     if (gst_query_get_n_allocation_params (query) > 0) {
564       gst_query_parse_nth_allocation_param (query, 0, &allocator, &params);
565       if (!GST_IS_VA_DMABUF_ALLOCATOR (allocator)
566           && !GST_IS_VA_ALLOCATOR (allocator))
567         gst_clear_object (&allocator);
568       update_allocator = TRUE;
569     } else {
570       gst_allocation_params_init (&params);
571     }
572
573     if (!allocator) {
574       if (!(allocator = _create_allocator (self, caps)))
575         return FALSE;
576     }
577
578     pool = _create_sinkpad_bufferpool (caps, size, 1, 0, usage_hint, allocator,
579         &params);
580     if (!pool) {
581       gst_object_unref (allocator);
582       goto config_failed;
583     }
584
585     if (update_allocator)
586       gst_query_set_nth_allocation_param (query, 0, allocator, &params);
587     else
588       gst_query_add_allocation_param (query, allocator, &params);
589
590     gst_query_add_allocation_pool (query, pool, size, 1, 0);
591
592     GST_DEBUG_OBJECT (self,
593         "proposing %" GST_PTR_FORMAT " with allocator %" GST_PTR_FORMAT,
594         pool, allocator);
595
596     gst_object_unref (allocator);
597     gst_object_unref (pool);
598
599     gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
600     gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
601   }
602
603   return TRUE;
604
605   /* ERRORS */
606 config_failed:
607   {
608     GST_ERROR_OBJECT (self, "failed to set config");
609     return FALSE;
610   }
611 }
612
613 static GstBufferPool *
614 _create_other_pool (GstAllocator * allocator,
615     GstAllocationParams * params, GstCaps * caps, guint size)
616 {
617   GstBufferPool *pool = NULL;
618   GstStructure *config;
619
620   pool = gst_video_buffer_pool_new ();
621   config = gst_buffer_pool_get_config (pool);
622
623   gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
624   gst_buffer_pool_config_set_allocator (config, allocator, params);
625   if (!gst_buffer_pool_set_config (pool, config)) {
626     gst_clear_object (&pool);
627   }
628
629   return pool;
630 }
631
632 /* configure the allocation query that was answered downstream, we can
633  * configure some properties on it. Only called when not in
634  * passthrough mode. */
635 static gboolean
636 gst_va_vpp_decide_allocation (GstBaseTransform * trans, GstQuery * query)
637 {
638   GstVaVpp *self = GST_VA_VPP (trans);
639   GstAllocator *allocator = NULL, *other_allocator = NULL;
640   GstAllocationParams params, other_params;
641   GstBufferPool *pool = NULL, *other_pool = NULL;
642   GstCaps *outcaps = NULL;
643   GstStructure *config;
644   GstVideoInfo vinfo;
645   guint min, max, size = 0, usage_hint = VA_SURFACE_ATTRIB_USAGE_HINT_VPP_WRITE;
646   gboolean update_pool, update_allocator, has_videometa, copy_frames;
647
648   gst_query_parse_allocation (query, &outcaps, NULL);
649
650   gst_allocation_params_init (&other_params);
651   gst_allocation_params_init (&params);
652
653   if (!gst_video_info_from_caps (&vinfo, outcaps)) {
654     GST_ERROR_OBJECT (self, "Cannot parse caps %" GST_PTR_FORMAT, outcaps);
655     return FALSE;
656   }
657
658   if (gst_query_get_n_allocation_params (query) > 0) {
659     gst_query_parse_nth_allocation_param (query, 0, &allocator, &other_params);
660     if (allocator && !(GST_IS_VA_DMABUF_ALLOCATOR (allocator)
661             || GST_IS_VA_ALLOCATOR (allocator))) {
662       /* save the allocator for the other pool */
663       other_allocator = allocator;
664       allocator = NULL;
665     }
666     update_allocator = TRUE;
667   } else {
668     update_allocator = FALSE;
669   }
670
671   if (gst_query_get_n_allocation_pools (query) > 0) {
672     gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
673
674     if (pool) {
675       if (!GST_IS_VA_POOL (pool)) {
676         GST_DEBUG_OBJECT (self,
677             "may need other pool for copy frames %" GST_PTR_FORMAT, pool);
678         other_pool = pool;
679         pool = NULL;
680       }
681     }
682
683     update_pool = TRUE;
684   } else {
685     size = GST_VIDEO_INFO_SIZE (&vinfo);
686     min = 1;
687     max = 0;
688     update_pool = FALSE;
689   }
690
691   if (!allocator) {
692     /* XXX(victor): VPP_WRITE uses a tiled drm modifier by iHD */
693     if (gst_caps_is_dmabuf (outcaps) && GST_VIDEO_INFO_IS_RGB (&vinfo))
694       usage_hint = VA_SURFACE_ATTRIB_USAGE_HINT_GENERIC;
695     if (!(allocator = _create_allocator (self, outcaps)))
696       return FALSE;
697   }
698
699   if (!pool)
700     pool = gst_va_pool_new ();
701
702   config = gst_buffer_pool_get_config (pool);
703   gst_buffer_pool_config_set_allocator (config, allocator, &params);
704   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
705   gst_buffer_pool_config_set_params (config, outcaps, size, min, max);
706   gst_buffer_pool_config_set_va_allocation_params (config, usage_hint);
707   gst_buffer_pool_set_config (pool, config);
708
709   if (update_allocator)
710     gst_query_set_nth_allocation_param (query, 0, allocator, &params);
711   else
712     gst_query_add_allocation_param (query, allocator, &params);
713
714   if (GST_IS_VA_DMABUF_ALLOCATOR (allocator)) {
715     gst_va_dmabuf_allocator_get_format (allocator, &vinfo, NULL);
716   } else if (GST_IS_VA_ALLOCATOR (allocator)) {
717     gst_va_allocator_get_format (allocator, &vinfo, NULL);
718   }
719   self->srcpad_info = vinfo;
720
721   if (update_pool)
722     gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
723   else
724     gst_query_add_allocation_pool (query, pool, size, min, max);
725
726   has_videometa = gst_query_find_allocation_meta (query,
727       GST_VIDEO_META_API_TYPE, NULL);
728
729   copy_frames = (!has_videometa && gst_va_pool_requires_video_meta (pool)
730       && gst_caps_is_raw (outcaps));
731   if (copy_frames) {
732     if (other_pool) {
733       gst_object_replace ((GstObject **) & self->other_pool,
734           (GstObject *) other_pool);
735     } else {
736       self->other_pool =
737           _create_other_pool (other_allocator, &other_params, outcaps, size);
738     }
739     GST_DEBUG_OBJECT (self, "Use the other pool for copy %" GST_PTR_FORMAT,
740         self->other_pool);
741   } else {
742     gst_clear_object (&self->other_pool);
743   }
744
745   GST_DEBUG_OBJECT (self,
746       "decided pool %" GST_PTR_FORMAT " with allocator %" GST_PTR_FORMAT,
747       pool, allocator);
748
749   gst_object_unref (allocator);
750   gst_object_unref (pool);
751   gst_clear_object (&other_allocator);
752   gst_clear_object (&other_pool);
753
754   /* removes allocation metas */
755   return GST_BASE_TRANSFORM_CLASS (parent_class)->decide_allocation (trans,
756       query);
757 }
758
759 static gboolean
760 gst_va_vpp_query (GstBaseTransform * trans, GstPadDirection direction,
761     GstQuery * query)
762 {
763   GstVaVpp *self = GST_VA_VPP (trans);
764   gboolean ret = FALSE;
765
766   switch (GST_QUERY_TYPE (query)) {
767     case GST_QUERY_CONTEXT:
768     {
769       GstVaDisplay *display = NULL;
770
771       gst_object_replace ((GstObject **) & display,
772           (GstObject *) self->display);
773       ret = gst_va_handle_context_query (GST_ELEMENT_CAST (self), query,
774           display);
775       gst_clear_object (&display);
776       break;
777     }
778     default:
779       ret = GST_BASE_TRANSFORM_CLASS (parent_class)->query (trans, direction,
780           query);
781       break;
782   }
783
784   return ret;
785 }
786
787 /* output buffers must be from our VA-based pool, they cannot be
788  * system-allocated */
789 static gboolean
790 gst_va_vpp_transform_size (GstBaseTransform * trans,
791     GstPadDirection direction, GstCaps * caps, gsize size,
792     GstCaps * othercaps, gsize * othersize)
793 {
794   return FALSE;
795 }
796
797 static gboolean
798 gst_va_vpp_set_caps (GstBaseTransform * trans, GstCaps * incaps,
799     GstCaps * outcaps)
800 {
801   GstVaVpp *self = GST_VA_VPP (trans);
802   GstVideoInfo in_info, out_info;
803   GstCapsFeatures *infeat, *outfeat;
804
805   /* input caps */
806   if (!gst_video_info_from_caps (&in_info, incaps))
807     goto invalid_caps;
808
809   /* output caps */
810   if (!gst_video_info_from_caps (&out_info, outcaps))
811     goto invalid_caps;
812
813   if (!gst_video_info_is_equal (&in_info, &out_info)) {
814     if (GST_VIDEO_INFO_FORMAT (&in_info) != GST_VIDEO_INFO_FORMAT (&out_info))
815       self->op_flags |= VPP_CONVERT_FORMAT;
816     else
817       self->op_flags &= ~VPP_CONVERT_FORMAT;
818
819     if (GST_VIDEO_INFO_WIDTH (&in_info) != GST_VIDEO_INFO_WIDTH (&out_info)
820         || GST_VIDEO_INFO_HEIGHT (&in_info) !=
821         GST_VIDEO_INFO_HEIGHT (&out_info))
822       self->op_flags |= VPP_CONVERT_SIZE;
823     else
824       self->op_flags &= ~VPP_CONVERT_SIZE;
825   } else {
826     self->op_flags &= ~VPP_CONVERT_FORMAT & ~VPP_CONVERT_SIZE;
827   }
828
829   infeat = gst_caps_get_features (incaps, 0);
830   outfeat = gst_caps_get_features (outcaps, 0);
831   if (!gst_caps_features_is_equal (infeat, outfeat))
832     self->op_flags |= VPP_CONVERT_FEATURE;
833   else
834     self->op_flags &= ~VPP_CONVERT_FEATURE;
835
836   if (self->sinkpad_pool) {
837     gst_buffer_pool_set_active (self->sinkpad_pool, FALSE);
838     gst_clear_object (&self->sinkpad_pool);
839   }
840
841   if (self->other_pool) {
842     gst_buffer_pool_set_active (self->other_pool, FALSE);
843     gst_clear_object (&self->other_pool);
844   }
845
846   gst_caps_replace (&self->incaps, incaps);
847   gst_caps_replace (&self->outcaps, outcaps);
848
849   self->in_info = in_info;
850   self->out_info = out_info;
851
852   self->negotiated =
853       gst_va_filter_set_formats (self->filter, &self->in_info, &self->out_info);
854
855   if (self->negotiated)
856     gst_va_vpp_update_passthrough (self, FALSE);
857
858   return self->negotiated;
859
860   /* ERRORS */
861 invalid_caps:
862   {
863     GST_ERROR_OBJECT (self, "invalid caps");
864     self->negotiated = FALSE;
865     return FALSE;
866   }
867 }
868
869 static inline gboolean
870 _get_filter_value (GstVaVpp * self, VAProcFilterType type, gfloat * value)
871 {
872   gboolean ret = TRUE;
873
874   GST_OBJECT_LOCK (self);
875   switch (type) {
876     case VAProcFilterNoiseReduction:
877       *value = self->denoise;
878       break;
879     case VAProcFilterSharpening:
880       *value = self->sharpen;
881       break;
882     case VAProcFilterSkinToneEnhancement:
883       *value = self->skintone;
884       break;
885     default:
886       ret = FALSE;
887       break;
888   }
889   GST_OBJECT_UNLOCK (self);
890
891   return ret;
892 }
893
894 static inline gboolean
895 _add_filter_buffer (GstVaVpp * self, VAProcFilterType type,
896     const VAProcFilterCap * cap)
897 {
898   VAProcFilterParameterBuffer param;
899   gfloat value = 0;
900
901   if (!_get_filter_value (self, type, &value))
902     return FALSE;
903   if (value == cap->range.default_value)
904     return FALSE;
905
906   /* *INDENT-OFF* */
907   param = (VAProcFilterParameterBuffer) {
908     .type = type,
909     .value = value,
910   };
911   /* *INDENT-ON* */
912
913   return
914       gst_va_filter_add_filter_buffer (self->filter, &param, sizeof (param), 1);
915 }
916
917 static inline gboolean
918 _get_filter_cb_value (GstVaVpp * self, VAProcColorBalanceType type,
919     gfloat * value)
920 {
921   gboolean ret = TRUE;
922
923   GST_OBJECT_LOCK (self);
924   switch (type) {
925     case VAProcColorBalanceHue:
926       *value = self->hue;
927       break;
928     case VAProcColorBalanceSaturation:
929       *value = self->saturation;
930       break;
931     case VAProcColorBalanceBrightness:
932       *value = self->brightness;
933       break;
934     case VAProcColorBalanceContrast:
935       *value = self->contrast;
936       break;
937     case VAProcColorBalanceAutoSaturation:
938       *value = self->auto_saturation;
939       break;
940     case VAProcColorBalanceAutoBrightness:
941       *value = self->auto_brightness;
942       break;
943     case VAProcColorBalanceAutoContrast:
944       *value = self->auto_contrast;
945       break;
946     default:
947       ret = FALSE;
948       break;
949   }
950   GST_OBJECT_UNLOCK (self);
951
952   return ret;
953 }
954
955 static inline gboolean
956 _add_filter_cb_buffer (GstVaVpp * self,
957     const VAProcFilterCapColorBalance * caps, guint num_caps)
958 {
959   VAProcFilterParameterBufferColorBalance param[VAProcColorBalanceCount] =
960       { 0, };
961   gfloat value;
962   guint i, c = 0;
963
964   value = 0;
965   for (i = 0; i < num_caps && i < VAProcColorBalanceCount; i++) {
966     if (!_get_filter_cb_value (self, caps[i].type, &value))
967       continue;
968     if (value == caps[i].range.default_value)
969       continue;
970
971     /* *INDENT-OFF* */
972     param[c++] = (VAProcFilterParameterBufferColorBalance) {
973       .type = VAProcFilterColorBalance,
974       .attrib = caps[i].type,
975       .value = value,
976     };
977     /* *INDENT-ON* */
978   }
979
980   if (c > 0) {
981     return gst_va_filter_add_filter_buffer (self->filter, param,
982         sizeof (*param), c);
983   }
984   return FALSE;
985 }
986
987 static void
988 _build_filters (GstVaVpp * self)
989 {
990   static const VAProcFilterType filter_types[] = { VAProcFilterNoiseReduction,
991     VAProcFilterSharpening, VAProcFilterSkinToneEnhancement,
992     VAProcFilterColorBalance,
993   };
994   guint i, num_caps;
995   gboolean apply = FALSE;
996
997   for (i = 0; i < G_N_ELEMENTS (filter_types); i++) {
998     const gpointer caps = gst_va_filter_get_filter_caps (self->filter,
999         filter_types[i], &num_caps);
1000     if (!caps)
1001       continue;
1002
1003     switch (filter_types[i]) {
1004       case VAProcFilterNoiseReduction:
1005         apply |= _add_filter_buffer (self, filter_types[i], caps);
1006         break;
1007       case VAProcFilterSharpening:
1008         apply |= _add_filter_buffer (self, filter_types[i], caps);
1009         break;
1010       case VAProcFilterSkinToneEnhancement:
1011         apply |= _add_filter_buffer (self, filter_types[i], caps);
1012         break;
1013       case VAProcFilterColorBalance:
1014         apply |= _add_filter_cb_buffer (self, caps, num_caps);
1015         break;
1016       default:
1017         break;
1018     }
1019   }
1020
1021   GST_OBJECT_LOCK (self);
1022   if (apply)
1023     self->op_flags |= VPP_CONVERT_FILTERS;
1024   else
1025     self->op_flags &= ~VPP_CONVERT_FILTERS;
1026   GST_OBJECT_UNLOCK (self);
1027 }
1028
1029 static void
1030 gst_va_vpp_rebuild_filters (GstVaVpp * self)
1031 {
1032   if (!g_atomic_int_get (&self->rebuild_filters))
1033     return;
1034
1035   gst_va_filter_drop_filter_buffers (self->filter);
1036   _build_filters (self);
1037   g_atomic_int_set (&self->rebuild_filters, FALSE);
1038 }
1039
1040 static void
1041 gst_va_vpp_before_transform (GstBaseTransform * trans, GstBuffer * inbuf)
1042 {
1043   GstVaVpp *self = GST_VA_VPP (trans);
1044   GstClockTime ts, stream_time;
1045
1046   ts = GST_BUFFER_TIMESTAMP (inbuf);
1047   stream_time =
1048       gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, ts);
1049
1050   GST_TRACE_OBJECT (self, "sync to %" GST_TIME_FORMAT, GST_TIME_ARGS (ts));
1051
1052   if (GST_CLOCK_TIME_IS_VALID (stream_time))
1053     gst_object_sync_values (GST_OBJECT (self), stream_time);
1054
1055   GST_OBJECT_LOCK (self);
1056   if (gst_buffer_get_video_crop_meta (inbuf)) {
1057     /* enable cropping if either already do operations on frame or
1058      * downstream doesn't support cropping */
1059     if (self->op_flags == 0 && self->forward_crop) {
1060       self->op_flags &= ~VPP_CONVERT_CROP;
1061     } else {
1062       self->op_flags |= VPP_CONVERT_CROP;
1063     }
1064   } else {
1065     self->op_flags &= ~VPP_CONVERT_CROP;
1066   }
1067   gst_va_filter_enable_cropping (self->filter,
1068       (self->op_flags & VPP_CONVERT_CROP));
1069   GST_OBJECT_UNLOCK (self);
1070
1071   gst_va_vpp_rebuild_filters (self);
1072   gst_va_vpp_update_passthrough (self, TRUE);
1073 }
1074
1075 static inline gsize
1076 _get_plane_data_size (GstVideoInfo * info, guint plane)
1077 {
1078   gint height, padded_height;
1079
1080   height = GST_VIDEO_INFO_HEIGHT (info);
1081   padded_height =
1082       GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info->finfo, plane, height);
1083
1084   return GST_VIDEO_INFO_PLANE_STRIDE (info, plane) * padded_height;
1085 }
1086
1087 static gboolean
1088 _try_import_dmabuf_unlocked (GstVaVpp * self, GstBuffer * inbuf)
1089 {
1090   GstVideoMeta *meta;
1091   GstVideoInfo in_info = self->in_info;
1092   GstMemory *mems[GST_VIDEO_MAX_PLANES];
1093   guint i, n_mem, n_planes;
1094   gsize offset[GST_VIDEO_MAX_PLANES];
1095   uintptr_t fd[GST_VIDEO_MAX_PLANES];
1096
1097   n_planes = GST_VIDEO_INFO_N_PLANES (&in_info);
1098   n_mem = gst_buffer_n_memory (inbuf);
1099   meta = gst_buffer_get_video_meta (inbuf);
1100
1101   /* This will eliminate most non-dmabuf out there */
1102   if (!gst_is_dmabuf_memory (gst_buffer_peek_memory (inbuf, 0)))
1103     return FALSE;
1104
1105   /* We cannot have multiple dmabuf per plane */
1106   if (n_mem > n_planes)
1107     return FALSE;
1108
1109   /* Update video info based on video meta */
1110   if (meta) {
1111     GST_VIDEO_INFO_WIDTH (&in_info) = meta->width;
1112     GST_VIDEO_INFO_HEIGHT (&in_info) = meta->height;
1113
1114     for (i = 0; i < meta->n_planes; i++) {
1115       GST_VIDEO_INFO_PLANE_OFFSET (&in_info, i) = meta->offset[i];
1116       GST_VIDEO_INFO_PLANE_STRIDE (&in_info, i) = meta->stride[i];
1117     }
1118   }
1119
1120   /* Find and validate all memories */
1121   for (i = 0; i < n_planes; i++) {
1122     guint plane_size;
1123     guint length;
1124     guint mem_idx;
1125     gsize mem_skip;
1126
1127     plane_size = _get_plane_data_size (&in_info, i);
1128
1129     if (!gst_buffer_find_memory (inbuf, in_info.offset[i], plane_size,
1130             &mem_idx, &length, &mem_skip))
1131       return FALSE;
1132
1133     /* We can't have more then one dmabuf per plane */
1134     if (length != 1)
1135       return FALSE;
1136
1137     mems[i] = gst_buffer_peek_memory (inbuf, mem_idx);
1138
1139     /* And all memory found must be dmabuf */
1140     if (!gst_is_dmabuf_memory (mems[i]))
1141       return FALSE;
1142
1143     offset[i] = mems[i]->offset + mem_skip;
1144     fd[i] = gst_dmabuf_memory_get_fd (mems[i]);
1145   }
1146
1147   /* Now create a VASurfaceID for the buffer */
1148   return gst_va_dmabuf_memories_setup (self->display, &in_info, n_planes, mems,
1149       fd, offset, VA_SURFACE_ATTRIB_USAGE_HINT_VPP_READ);
1150 }
1151
1152 static GstBufferPool *
1153 _get_sinkpad_pool (GstVaVpp * self)
1154 {
1155   GstAllocator *allocator;
1156   GstAllocationParams params;
1157   GstCaps *caps;
1158   GstVideoInfo alloc_info, in_info;
1159   guint size, usage_hint = VA_SURFACE_ATTRIB_USAGE_HINT_VPP_READ;
1160
1161   if (self->sinkpad_pool)
1162     return self->sinkpad_pool;
1163
1164   gst_allocation_params_init (&params);
1165
1166   if (self->alloccaps) {
1167     caps = self->alloccaps;
1168     gst_video_info_from_caps (&in_info, caps);
1169   } else {
1170     caps = self->incaps;
1171     in_info = self->in_info;
1172   }
1173
1174   size = GST_VIDEO_INFO_SIZE (&in_info);
1175
1176   allocator = _create_allocator (self, caps);
1177
1178   self->sinkpad_pool = _create_sinkpad_bufferpool (caps, size, 1, 0, usage_hint,
1179       allocator, &params);
1180
1181   if (GST_IS_VA_DMABUF_ALLOCATOR (allocator)) {
1182     if (!gst_va_dmabuf_allocator_get_format (allocator, &alloc_info, NULL))
1183       alloc_info = in_info;
1184   } else if (GST_IS_VA_ALLOCATOR (allocator)) {
1185     if (!gst_va_allocator_get_format (allocator, &alloc_info, NULL))
1186       alloc_info = in_info;
1187   }
1188
1189   gst_object_unref (allocator);
1190
1191   if (self->sinkpad_pool) {
1192     self->sinkpad_info = alloc_info;
1193     gst_buffer_pool_set_active (self->sinkpad_pool, TRUE);
1194   }
1195
1196   return self->sinkpad_pool;
1197 }
1198
1199 static gboolean
1200 _try_import_buffer (GstVaVpp * self, GstBuffer * inbuf)
1201 {
1202   VASurfaceID surface;
1203   gboolean ret;
1204
1205   surface = gst_va_buffer_get_surface (inbuf);
1206   if (surface != VA_INVALID_ID)
1207     return TRUE;
1208
1209   g_rec_mutex_lock (&GST_VA_SHARED_LOCK);
1210   ret = _try_import_dmabuf_unlocked (self, inbuf);
1211   g_rec_mutex_unlock (&GST_VA_SHARED_LOCK);
1212
1213   return ret;
1214 }
1215
1216 static GstFlowReturn
1217 gst_va_vpp_import_input_buffer (GstVaVpp * self, GstBuffer * inbuf,
1218     GstBuffer ** buf)
1219 {
1220   GstBuffer *buffer = NULL;
1221   GstBufferPool *pool;
1222   GstFlowReturn ret;
1223   GstVideoFrame in_frame, out_frame;
1224   gboolean imported, copied;
1225
1226   imported = _try_import_buffer (self, inbuf);
1227   if (imported) {
1228     *buf = gst_buffer_ref (inbuf);
1229     return GST_FLOW_OK;
1230   }
1231
1232   /* input buffer doesn't come from a vapool, thus it is required to
1233    * have a pool, grab from it a new buffer and copy the input
1234    * buffer to the new one */
1235   if (!(pool = _get_sinkpad_pool (self)))
1236     return GST_FLOW_ERROR;
1237
1238   ret = gst_buffer_pool_acquire_buffer (pool, &buffer, NULL);
1239   if (ret != GST_FLOW_OK)
1240     return ret;
1241
1242   GST_LOG_OBJECT (self, "copying input frame");
1243
1244   if (!gst_video_frame_map (&in_frame, &self->in_info, inbuf, GST_MAP_READ))
1245     goto invalid_buffer;
1246
1247   if (!gst_video_frame_map (&out_frame, &self->sinkpad_info, buffer,
1248           GST_MAP_WRITE)) {
1249     gst_video_frame_unmap (&in_frame);
1250     goto invalid_buffer;
1251   }
1252
1253   copied = gst_video_frame_copy (&out_frame, &in_frame);
1254
1255   gst_video_frame_unmap (&out_frame);
1256   gst_video_frame_unmap (&in_frame);
1257
1258   if (!copied)
1259     goto invalid_buffer;
1260
1261   /* strictly speaking this is not needed but let's play safe */
1262   if (!gst_buffer_copy_into (buffer, inbuf, GST_BUFFER_COPY_FLAGS |
1263           GST_BUFFER_COPY_TIMESTAMPS, 0, -1))
1264     return GST_FLOW_ERROR;
1265
1266   *buf = buffer;
1267
1268   return GST_FLOW_OK;
1269
1270 invalid_buffer:
1271   {
1272     GST_ELEMENT_WARNING (self, CORE, NOT_IMPLEMENTED, (NULL),
1273         ("invalid video buffer received"));
1274     if (buffer)
1275       gst_buffer_unref (buffer);
1276     return GST_FLOW_OK;
1277   }
1278 }
1279
1280 static GstFlowReturn
1281 gst_va_vpp_transform (GstBaseTransform * trans, GstBuffer * inbuf,
1282     GstBuffer * outbuf)
1283 {
1284   GstVaVpp *self = GST_VA_VPP (trans);
1285   GstBuffer *buf = NULL;
1286   GstFlowReturn res = GST_FLOW_OK;
1287   GstVaSample src, dst;
1288
1289   if (G_UNLIKELY (!self->negotiated))
1290     goto unknown_format;
1291
1292   res = gst_va_vpp_import_input_buffer (self, inbuf, &buf);
1293   if (res != GST_FLOW_OK)
1294     return res;
1295
1296   /* *INDENT-OFF* */
1297   src = (GstVaSample) {
1298     .buffer = buf,
1299   };
1300
1301   dst = (GstVaSample) {
1302     .buffer = outbuf,
1303   };
1304   /* *INDENT-ON* */
1305
1306   if (!gst_va_filter_convert_surface (self->filter, &src, &dst)) {
1307     gst_buffer_set_flags (outbuf, GST_BUFFER_FLAG_CORRUPTED);
1308   }
1309
1310   gst_buffer_unref (buf);
1311
1312   return res;
1313
1314   /* ERRORS */
1315 unknown_format:
1316   {
1317     GST_ELEMENT_ERROR (self, CORE, NOT_IMPLEMENTED, (NULL), ("unknown format"));
1318     return GST_FLOW_NOT_NEGOTIATED;
1319   }
1320 }
1321
1322 static gboolean
1323 gst_va_vpp_transform_meta (GstBaseTransform * trans, GstBuffer * inbuf,
1324     GstMeta * meta, GstBuffer * outbuf)
1325 {
1326   GstVaVpp *self = GST_VA_VPP (trans);
1327   const GstMetaInfo *info = meta->info;
1328   const gchar *const *tags;
1329
1330   tags = gst_meta_api_type_get_tags (info->api);
1331
1332   if (!tags)
1333     return TRUE;
1334
1335   /* don't copy colorspace/size/orientation specific metadata */
1336   if ((self->op_flags & VPP_CONVERT_FORMAT)
1337       && gst_meta_api_type_has_tag (info->api, META_TAG_COLORSPACE))
1338     return FALSE;
1339   else if ((self->op_flags & (VPP_CONVERT_SIZE | VPP_CONVERT_CROP))
1340       && gst_meta_api_type_has_tag (info->api, META_TAG_SIZE))
1341     return FALSE;
1342   else if ((self->op_flags & VPP_CONVERT_DIRECTION)
1343       && gst_meta_api_type_has_tag (info->api, META_TAG_ORIENTATION))
1344     return FALSE;
1345   else if (gst_meta_api_type_has_tag (info->api, META_TAG_VIDEO))
1346     return TRUE;
1347
1348   return GST_BASE_TRANSFORM_CLASS (parent_class)->transform_meta (trans, outbuf,
1349       meta, inbuf);
1350 }
1351
1352 /* Remove all the info for the cases when we can actually convert:
1353  * Delete all the video "format", rangify the resolution size, also
1354  * remove "colorimetry", "chroma-site" and "pixel-aspect-ratio".  All
1355  * the missing caps features should be added based on the template,
1356  * and the caps features' order in @caps is kept */
1357 static GstCaps *
1358 gst_va_vpp_complete_caps_features (GstCaps * caps, GstCaps * tmpl_caps)
1359 {
1360   GstCaps *ret, *full_caps;
1361   GstStructure *structure;
1362   GstCapsFeatures *features;
1363   gboolean has_sys_mem = FALSE, has_dma = FALSE, has_va = FALSE;
1364   gint i, n;
1365
1366   full_caps = gst_caps_new_empty ();
1367
1368   n = gst_caps_get_size (caps);
1369   for (i = 0; i < n; i++) {
1370     structure = gst_caps_get_structure (caps, i);
1371     features = gst_caps_get_features (caps, i);
1372
1373     /* If this is already expressed by the existing caps
1374      * skip this structure */
1375     if (i > 0
1376         && gst_caps_is_subset_structure_full (full_caps, structure, features))
1377       continue;
1378
1379     if (gst_caps_features_is_any (features))
1380       continue;
1381
1382     if (gst_caps_features_is_equal (features,
1383             GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY)) {
1384       has_sys_mem = TRUE;
1385     } else {
1386       gboolean valid = FALSE;
1387
1388       if (gst_caps_features_contains (features, GST_CAPS_FEATURE_MEMORY_DMABUF)) {
1389         has_dma = TRUE;
1390         valid = TRUE;
1391       }
1392       if (gst_caps_features_contains (features, "memory:VAMemory")) {
1393         has_va = TRUE;
1394         valid = TRUE;
1395       }
1396       /* Not contain our supported feature */
1397       if (!valid)
1398         continue;
1399     }
1400
1401     structure = gst_structure_copy (structure);
1402
1403     gst_structure_set (structure, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1404         "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
1405     /* if pixel aspect ratio, make a range of it */
1406     if (gst_structure_has_field (structure, "pixel-aspect-ratio")) {
1407       gst_structure_set (structure, "pixel-aspect-ratio",
1408           GST_TYPE_FRACTION_RANGE, 1, G_MAXINT, G_MAXINT, 1, NULL);
1409     }
1410     gst_structure_remove_fields (structure, "format", "colorimetry",
1411         "chroma-site", NULL);
1412
1413     gst_caps_append_structure_full (full_caps, structure,
1414         gst_caps_features_copy (features));
1415   }
1416
1417   /* Adding the missing features. */
1418   n = gst_caps_get_size (tmpl_caps);
1419   for (i = 0; i < n; i++) {
1420     structure = gst_caps_get_structure (tmpl_caps, i);
1421     features = gst_caps_get_features (tmpl_caps, i);
1422
1423     if (gst_caps_features_contains (features, "memory:VAMemory") && !has_va)
1424       gst_caps_append_structure_full (full_caps, gst_structure_copy (structure),
1425           gst_caps_features_copy (features));
1426
1427     if (gst_caps_features_contains (features,
1428             GST_CAPS_FEATURE_MEMORY_DMABUF) && !has_dma)
1429       gst_caps_append_structure_full (full_caps, gst_structure_copy (structure),
1430           gst_caps_features_copy (features));
1431
1432     if (gst_caps_features_is_equal (features,
1433             GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY) && !has_sys_mem)
1434       gst_caps_append_structure_full (full_caps, gst_structure_copy (structure),
1435           gst_caps_features_copy (features));
1436   }
1437
1438   ret = gst_caps_intersect_full (full_caps, tmpl_caps,
1439       GST_CAPS_INTERSECT_FIRST);
1440   gst_caps_unref (full_caps);
1441
1442   return ret;
1443 }
1444
1445 static GstCaps *
1446 gst_va_vpp_transform_caps (GstBaseTransform * trans, GstPadDirection direction,
1447     GstCaps * caps, GstCaps * filter)
1448 {
1449   GstVaVpp *self = GST_VA_VPP (trans);
1450   GstCaps *ret, *tmpl_caps;
1451
1452   GST_DEBUG_OBJECT (self,
1453       "Transforming caps %" GST_PTR_FORMAT " in direction %s", caps,
1454       (direction == GST_PAD_SINK) ? "sink" : "src");
1455
1456   if (direction == GST_PAD_SINK) {
1457     tmpl_caps =
1458         gst_pad_get_pad_template_caps (GST_BASE_TRANSFORM_SRC_PAD (trans));
1459   } else {
1460     tmpl_caps =
1461         gst_pad_get_pad_template_caps (GST_BASE_TRANSFORM_SINK_PAD (trans));
1462   }
1463
1464   ret = gst_va_vpp_complete_caps_features (caps, tmpl_caps);
1465
1466   gst_caps_unref (tmpl_caps);
1467
1468   if (filter) {
1469     GstCaps *intersection;
1470
1471     intersection =
1472         gst_caps_intersect_full (filter, ret, GST_CAPS_INTERSECT_FIRST);
1473     gst_caps_unref (ret);
1474     ret = intersection;
1475   }
1476
1477   GST_DEBUG_OBJECT (trans, "returning caps: %" GST_PTR_FORMAT, ret);
1478
1479   return ret;
1480 }
1481
1482 /*
1483  * This is an incomplete matrix of in formats and a score for the preferred output
1484  * format.
1485  *
1486  *         out: RGB24   RGB16  ARGB  AYUV  YUV444  YUV422 YUV420 YUV411 YUV410  PAL  GRAY
1487  *  in
1488  * RGB24          0      2       1     2     2       3      4      5      6      7    8
1489  * RGB16          1      0       1     2     2       3      4      5      6      7    8
1490  * ARGB           2      3       0     1     4       5      6      7      8      9    10
1491  * AYUV           3      4       1     0     2       5      6      7      8      9    10
1492  * YUV444         2      4       3     1     0       5      6      7      8      9    10
1493  * YUV422         3      5       4     2     1       0      6      7      8      9    10
1494  * YUV420         4      6       5     3     2       1      0      7      8      9    10
1495  * YUV411         4      6       5     3     2       1      7      0      8      9    10
1496  * YUV410         6      8       7     5     4       3      2      1      0      9    10
1497  * PAL            1      3       2     6     4       6      7      8      9      0    10
1498  * GRAY           1      4       3     2     1       5      6      7      8      9    0
1499  *
1500  * PAL or GRAY are never preferred, if we can we would convert to PAL instead
1501  * of GRAY, though
1502  * less subsampling is preferred and if any, preferably horizontal
1503  * We would like to keep the alpha, even if we would need to to colorspace conversion
1504  * or lose depth.
1505  */
1506 #define SCORE_FORMAT_CHANGE       1
1507 #define SCORE_DEPTH_CHANGE        1
1508 #define SCORE_ALPHA_CHANGE        1
1509 #define SCORE_CHROMA_W_CHANGE     1
1510 #define SCORE_CHROMA_H_CHANGE     1
1511 #define SCORE_PALETTE_CHANGE      1
1512
1513 #define SCORE_COLORSPACE_LOSS     2     /* RGB <-> YUV */
1514 #define SCORE_DEPTH_LOSS          4     /* change bit depth */
1515 #define SCORE_ALPHA_LOSS          8     /* lose the alpha channel */
1516 #define SCORE_CHROMA_W_LOSS      16     /* vertical subsample */
1517 #define SCORE_CHROMA_H_LOSS      32     /* horizontal subsample */
1518 #define SCORE_PALETTE_LOSS       64     /* convert to palette format */
1519 #define SCORE_COLOR_LOSS        128     /* convert to GRAY */
1520
1521 #define COLORSPACE_MASK (GST_VIDEO_FORMAT_FLAG_YUV | \
1522                          GST_VIDEO_FORMAT_FLAG_RGB | GST_VIDEO_FORMAT_FLAG_GRAY)
1523 #define ALPHA_MASK      (GST_VIDEO_FORMAT_FLAG_ALPHA)
1524 #define PALETTE_MASK    (GST_VIDEO_FORMAT_FLAG_PALETTE)
1525
1526 /* calculate how much loss a conversion would be */
1527 static void
1528 score_value (GstVaVpp * self, const GstVideoFormatInfo * in_info,
1529     const GValue * val, gint * min_loss, const GstVideoFormatInfo ** out_info)
1530 {
1531   const gchar *fname;
1532   const GstVideoFormatInfo *t_info;
1533   GstVideoFormatFlags in_flags, t_flags;
1534   gint loss;
1535
1536   fname = g_value_get_string (val);
1537   t_info = gst_video_format_get_info (gst_video_format_from_string (fname));
1538   if (!t_info || t_info->format == GST_VIDEO_FORMAT_UNKNOWN)
1539     return;
1540
1541   /* accept input format immediately without loss */
1542   if (in_info == t_info) {
1543     *min_loss = 0;
1544     *out_info = t_info;
1545     return;
1546   }
1547
1548   loss = SCORE_FORMAT_CHANGE;
1549
1550   in_flags = GST_VIDEO_FORMAT_INFO_FLAGS (in_info);
1551   in_flags &= ~GST_VIDEO_FORMAT_FLAG_LE;
1552   in_flags &= ~GST_VIDEO_FORMAT_FLAG_COMPLEX;
1553   in_flags &= ~GST_VIDEO_FORMAT_FLAG_UNPACK;
1554
1555   t_flags = GST_VIDEO_FORMAT_INFO_FLAGS (t_info);
1556   t_flags &= ~GST_VIDEO_FORMAT_FLAG_LE;
1557   t_flags &= ~GST_VIDEO_FORMAT_FLAG_COMPLEX;
1558   t_flags &= ~GST_VIDEO_FORMAT_FLAG_UNPACK;
1559
1560   if ((t_flags & PALETTE_MASK) != (in_flags & PALETTE_MASK)) {
1561     loss += SCORE_PALETTE_CHANGE;
1562     if (t_flags & PALETTE_MASK)
1563       loss += SCORE_PALETTE_LOSS;
1564   }
1565
1566   if ((t_flags & COLORSPACE_MASK) != (in_flags & COLORSPACE_MASK)) {
1567     loss += SCORE_COLORSPACE_LOSS;
1568     if (t_flags & GST_VIDEO_FORMAT_FLAG_GRAY)
1569       loss += SCORE_COLOR_LOSS;
1570   }
1571
1572   if ((t_flags & ALPHA_MASK) != (in_flags & ALPHA_MASK)) {
1573     loss += SCORE_ALPHA_CHANGE;
1574     if (in_flags & ALPHA_MASK)
1575       loss += SCORE_ALPHA_LOSS;
1576   }
1577
1578   if ((in_info->h_sub[1]) != (t_info->h_sub[1])) {
1579     loss += SCORE_CHROMA_H_CHANGE;
1580     if ((in_info->h_sub[1]) < (t_info->h_sub[1]))
1581       loss += SCORE_CHROMA_H_LOSS;
1582   }
1583   if ((in_info->w_sub[1]) != (t_info->w_sub[1])) {
1584     loss += SCORE_CHROMA_W_CHANGE;
1585     if ((in_info->w_sub[1]) < (t_info->w_sub[1]))
1586       loss += SCORE_CHROMA_W_LOSS;
1587   }
1588
1589   if ((in_info->bits) != (t_info->bits)) {
1590     loss += SCORE_DEPTH_CHANGE;
1591     if ((in_info->bits) > (t_info->bits))
1592       loss += SCORE_DEPTH_LOSS;
1593   }
1594
1595   GST_DEBUG_OBJECT (self, "score %s -> %s = %d",
1596       GST_VIDEO_FORMAT_INFO_NAME (in_info),
1597       GST_VIDEO_FORMAT_INFO_NAME (t_info), loss);
1598
1599   if (loss < *min_loss) {
1600     GST_DEBUG_OBJECT (self, "found new best %d", loss);
1601     *out_info = t_info;
1602     *min_loss = loss;
1603   }
1604 }
1605
1606 static void
1607 gst_va_vpp_fixate_format (GstVaVpp * self, GstCaps * caps, GstCaps * result)
1608 {
1609   GstStructure *ins, *outs;
1610   const gchar *in_format;
1611   const GstVideoFormatInfo *in_info, *out_info = NULL;
1612   gint min_loss = G_MAXINT;
1613   guint i, capslen;
1614
1615   ins = gst_caps_get_structure (caps, 0);
1616   in_format = gst_structure_get_string (ins, "format");
1617   if (!in_format)
1618     return;
1619
1620   GST_DEBUG_OBJECT (self, "source format %s", in_format);
1621
1622   in_info =
1623       gst_video_format_get_info (gst_video_format_from_string (in_format));
1624   if (!in_info)
1625     return;
1626
1627   outs = gst_caps_get_structure (result, 0);
1628
1629   capslen = gst_caps_get_size (result);
1630   GST_DEBUG_OBJECT (self, "iterate %d structures", capslen);
1631   for (i = 0; i < capslen; i++) {
1632     GstStructure *tests;
1633     const GValue *format;
1634
1635     tests = gst_caps_get_structure (result, i);
1636     format = gst_structure_get_value (tests, "format");
1637     gst_structure_remove_fields (tests, "height", "width", "pixel-aspect-ratio",
1638         "display-aspect-ratio", NULL);
1639     /* should not happen */
1640     if (format == NULL)
1641       continue;
1642
1643     if (GST_VALUE_HOLDS_LIST (format)) {
1644       gint j, len;
1645
1646       len = gst_value_list_get_size (format);
1647       GST_DEBUG_OBJECT (self, "have %d formats", len);
1648       for (j = 0; j < len; j++) {
1649         const GValue *val;
1650
1651         val = gst_value_list_get_value (format, j);
1652         if (G_VALUE_HOLDS_STRING (val)) {
1653           score_value (self, in_info, val, &min_loss, &out_info);
1654           if (min_loss == 0)
1655             break;
1656         }
1657       }
1658     } else if (G_VALUE_HOLDS_STRING (format)) {
1659       score_value (self, in_info, format, &min_loss, &out_info);
1660     }
1661   }
1662   if (out_info)
1663     gst_structure_set (outs, "format", G_TYPE_STRING,
1664         GST_VIDEO_FORMAT_INFO_NAME (out_info), NULL);
1665 }
1666
1667 static GstCaps *
1668 gst_va_vpp_get_fixed_format (GstVaVpp * self, GstPadDirection direction,
1669     GstCaps * caps, GstCaps * othercaps)
1670 {
1671   GstCaps *result;
1672
1673   result = gst_caps_intersect (othercaps, caps);
1674   if (gst_caps_is_empty (result)) {
1675     gst_caps_unref (result);
1676     result = gst_caps_copy (othercaps);
1677   }
1678
1679   gst_va_vpp_fixate_format (self, caps, result);
1680
1681   /* fixate remaining fields */
1682   result = gst_caps_fixate (result);
1683
1684   if (direction == GST_PAD_SINK) {
1685     if (gst_caps_is_subset (caps, result)) {
1686       gst_caps_replace (&result, caps);
1687     }
1688   }
1689
1690   return result;
1691 }
1692
1693 static GstCaps *
1694 gst_va_vpp_fixate_size (GstVaVpp * self, GstPadDirection direction,
1695     GstCaps * caps, GstCaps * othercaps)
1696 {
1697   GstStructure *ins, *outs;
1698   const GValue *from_par, *to_par;
1699   GValue fpar = { 0, };
1700   GValue tpar = { 0, };
1701
1702   othercaps = gst_caps_truncate (othercaps);
1703   othercaps = gst_caps_make_writable (othercaps);
1704   ins = gst_caps_get_structure (caps, 0);
1705   outs = gst_caps_get_structure (othercaps, 0);
1706
1707   from_par = gst_structure_get_value (ins, "pixel-aspect-ratio");
1708   to_par = gst_structure_get_value (outs, "pixel-aspect-ratio");
1709
1710   /* If we're fixating from the sinkpad we always set the PAR and
1711    * assume that missing PAR on the sinkpad means 1/1 and
1712    * missing PAR on the srcpad means undefined
1713    */
1714   if (direction == GST_PAD_SINK) {
1715     if (!from_par) {
1716       g_value_init (&fpar, GST_TYPE_FRACTION);
1717       gst_value_set_fraction (&fpar, 1, 1);
1718       from_par = &fpar;
1719     }
1720     if (!to_par) {
1721       g_value_init (&tpar, GST_TYPE_FRACTION_RANGE);
1722       gst_value_set_fraction_range_full (&tpar, 1, G_MAXINT, G_MAXINT, 1);
1723       to_par = &tpar;
1724     }
1725   } else {
1726     if (!to_par) {
1727       g_value_init (&tpar, GST_TYPE_FRACTION);
1728       gst_value_set_fraction (&tpar, 1, 1);
1729       to_par = &tpar;
1730
1731       gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
1732           NULL);
1733     }
1734     if (!from_par) {
1735       g_value_init (&fpar, GST_TYPE_FRACTION);
1736       gst_value_set_fraction (&fpar, 1, 1);
1737       from_par = &fpar;
1738     }
1739   }
1740
1741   /* we have both PAR but they might not be fixated */
1742   {
1743     gint from_w, from_h, from_par_n, from_par_d, to_par_n, to_par_d;
1744     gint w = 0, h = 0;
1745     gint from_dar_n, from_dar_d;
1746     gint num, den;
1747
1748     /* from_par should be fixed */
1749     g_return_val_if_fail (gst_value_is_fixed (from_par), othercaps);
1750
1751     from_par_n = gst_value_get_fraction_numerator (from_par);
1752     from_par_d = gst_value_get_fraction_denominator (from_par);
1753
1754     gst_structure_get_int (ins, "width", &from_w);
1755     gst_structure_get_int (ins, "height", &from_h);
1756
1757     gst_structure_get_int (outs, "width", &w);
1758     gst_structure_get_int (outs, "height", &h);
1759
1760     /* if video-orientation changes */
1761     switch (gst_va_filter_get_orientation (self->filter)) {
1762       case GST_VIDEO_ORIENTATION_90R:
1763       case GST_VIDEO_ORIENTATION_90L:
1764       case GST_VIDEO_ORIENTATION_UL_LR:
1765       case GST_VIDEO_ORIENTATION_UR_LL:
1766         if (direction == GST_PAD_SINK) {
1767           SWAP (from_w, from_h);
1768           SWAP (from_par_n, from_par_d);
1769         } else if (direction == GST_PAD_SRC) {
1770           SWAP (w, h);
1771           /* there's no need to swap 1/1 par */
1772         }
1773         break;
1774       default:
1775         break;
1776     }
1777
1778     /* if both width and height are already fixed, we can't do anything
1779      * about it anymore */
1780     if (w && h) {
1781       guint n, d;
1782
1783       GST_DEBUG_OBJECT (self, "dimensions already set to %dx%d, not fixating",
1784           w, h);
1785       if (!gst_value_is_fixed (to_par)) {
1786         if (gst_video_calculate_display_ratio (&n, &d, from_w, from_h,
1787                 from_par_n, from_par_d, w, h)) {
1788           GST_DEBUG_OBJECT (self, "fixating to_par to %dx%d", n, d);
1789           if (gst_structure_has_field (outs, "pixel-aspect-ratio"))
1790             gst_structure_fixate_field_nearest_fraction (outs,
1791                 "pixel-aspect-ratio", n, d);
1792           else if (n != d)
1793             gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1794                 n, d, NULL);
1795         }
1796       }
1797       goto done;
1798     }
1799
1800     /* Calculate input DAR */
1801     if (!gst_util_fraction_multiply (from_w, from_h, from_par_n, from_par_d,
1802             &from_dar_n, &from_dar_d)) {
1803       GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1804           ("Error calculating the output scaled size - integer overflow"));
1805       goto done;
1806     }
1807
1808     GST_DEBUG_OBJECT (self, "Input DAR is %d/%d", from_dar_n, from_dar_d);
1809
1810     /* If either width or height are fixed there's not much we
1811      * can do either except choosing a height or width and PAR
1812      * that matches the DAR as good as possible
1813      */
1814     if (h) {
1815       GstStructure *tmp;
1816       gint set_w, set_par_n, set_par_d;
1817
1818       GST_DEBUG_OBJECT (self, "height is fixed (%d)", h);
1819
1820       /* If the PAR is fixed too, there's not much to do
1821        * except choosing the width that is nearest to the
1822        * width with the same DAR */
1823       if (gst_value_is_fixed (to_par)) {
1824         to_par_n = gst_value_get_fraction_numerator (to_par);
1825         to_par_d = gst_value_get_fraction_denominator (to_par);
1826
1827         GST_DEBUG_OBJECT (self, "PAR is fixed %d/%d", to_par_n, to_par_d);
1828
1829         if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_d,
1830                 to_par_n, &num, &den)) {
1831           GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1832               ("Error calculating the output scaled size - integer overflow"));
1833           goto done;
1834         }
1835
1836         w = (guint) gst_util_uint64_scale_int_round (h, num, den);
1837         gst_structure_fixate_field_nearest_int (outs, "width", w);
1838
1839         goto done;
1840       }
1841
1842       /* The PAR is not fixed and it's quite likely that we can set
1843        * an arbitrary PAR. */
1844
1845       /* Check if we can keep the input width */
1846       tmp = gst_structure_copy (outs);
1847       gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
1848       gst_structure_get_int (tmp, "width", &set_w);
1849
1850       /* Might have failed but try to keep the DAR nonetheless by
1851        * adjusting the PAR */
1852       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, h, set_w,
1853               &to_par_n, &to_par_d)) {
1854         GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1855             ("Error calculating the output scaled size - integer overflow"));
1856         gst_structure_free (tmp);
1857         goto done;
1858       }
1859
1860       if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
1861         gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
1862       gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
1863           to_par_n, to_par_d);
1864       gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
1865           &set_par_d);
1866       gst_structure_free (tmp);
1867
1868       /* Check if the adjusted PAR is accepted */
1869       if (set_par_n == to_par_n && set_par_d == to_par_d) {
1870         if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1871             set_par_n != set_par_d)
1872           gst_structure_set (outs, "width", G_TYPE_INT, set_w,
1873               "pixel-aspect-ratio", GST_TYPE_FRACTION, set_par_n, set_par_d,
1874               NULL);
1875         goto done;
1876       }
1877
1878       /* Otherwise scale the width to the new PAR and check if the
1879        * adjusted with is accepted. If all that fails we can't keep
1880        * the DAR */
1881       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
1882               set_par_n, &num, &den)) {
1883         GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1884             ("Error calculating the output scaled size - integer overflow"));
1885         goto done;
1886       }
1887
1888       w = (guint) gst_util_uint64_scale_int_round (h, num, den);
1889       gst_structure_fixate_field_nearest_int (outs, "width", w);
1890       if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1891           set_par_n != set_par_d)
1892         gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1893             set_par_n, set_par_d, NULL);
1894
1895       goto done;
1896     } else if (w) {
1897       GstStructure *tmp;
1898       gint set_h, set_par_n, set_par_d;
1899
1900       GST_DEBUG_OBJECT (self, "width is fixed (%d)", w);
1901
1902       /* If the PAR is fixed too, there's not much to do
1903        * except choosing the height that is nearest to the
1904        * height with the same DAR */
1905       if (gst_value_is_fixed (to_par)) {
1906         to_par_n = gst_value_get_fraction_numerator (to_par);
1907         to_par_d = gst_value_get_fraction_denominator (to_par);
1908
1909         GST_DEBUG_OBJECT (self, "PAR is fixed %d/%d", to_par_n, to_par_d);
1910
1911         if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_d,
1912                 to_par_n, &num, &den)) {
1913           GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1914               ("Error calculating the output scaled size - integer overflow"));
1915           goto done;
1916         }
1917
1918         h = (guint) gst_util_uint64_scale_int_round (w, den, num);
1919         gst_structure_fixate_field_nearest_int (outs, "height", h);
1920
1921         goto done;
1922       }
1923
1924       /* The PAR is not fixed and it's quite likely that we can set
1925        * an arbitrary PAR. */
1926
1927       /* Check if we can keep the input height */
1928       tmp = gst_structure_copy (outs);
1929       gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
1930       gst_structure_get_int (tmp, "height", &set_h);
1931
1932       /* Might have failed but try to keep the DAR nonetheless by
1933        * adjusting the PAR */
1934       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_h, w,
1935               &to_par_n, &to_par_d)) {
1936         GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1937             ("Error calculating the output scaled size - integer overflow"));
1938         gst_structure_free (tmp);
1939         goto done;
1940       }
1941       if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
1942         gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
1943       gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
1944           to_par_n, to_par_d);
1945       gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
1946           &set_par_d);
1947       gst_structure_free (tmp);
1948
1949       /* Check if the adjusted PAR is accepted */
1950       if (set_par_n == to_par_n && set_par_d == to_par_d) {
1951         if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1952             set_par_n != set_par_d)
1953           gst_structure_set (outs, "height", G_TYPE_INT, set_h,
1954               "pixel-aspect-ratio", GST_TYPE_FRACTION, set_par_n, set_par_d,
1955               NULL);
1956         goto done;
1957       }
1958
1959       /* Otherwise scale the height to the new PAR and check if the
1960        * adjusted with is accepted. If all that fails we can't keep
1961        * the DAR */
1962       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
1963               set_par_n, &num, &den)) {
1964         GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1965             ("Error calculating the output scale sized - integer overflow"));
1966         goto done;
1967       }
1968
1969       h = (guint) gst_util_uint64_scale_int_round (w, den, num);
1970       gst_structure_fixate_field_nearest_int (outs, "height", h);
1971       if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1972           set_par_n != set_par_d)
1973         gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1974             set_par_n, set_par_d, NULL);
1975
1976       goto done;
1977     } else if (gst_value_is_fixed (to_par)) {
1978       GstStructure *tmp;
1979       gint set_h, set_w, f_h, f_w;
1980
1981       to_par_n = gst_value_get_fraction_numerator (to_par);
1982       to_par_d = gst_value_get_fraction_denominator (to_par);
1983
1984       /* Calculate scale factor for the PAR change */
1985       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_n,
1986               to_par_d, &num, &den)) {
1987         GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1988             ("Error calculating the output scaled size - integer overflow"));
1989         goto done;
1990       }
1991
1992       /* Try to keep the input height (because of interlacing) */
1993       tmp = gst_structure_copy (outs);
1994       gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
1995       gst_structure_get_int (tmp, "height", &set_h);
1996
1997       /* This might have failed but try to scale the width
1998        * to keep the DAR nonetheless */
1999       w = (guint) gst_util_uint64_scale_int_round (set_h, num, den);
2000       gst_structure_fixate_field_nearest_int (tmp, "width", w);
2001       gst_structure_get_int (tmp, "width", &set_w);
2002       gst_structure_free (tmp);
2003
2004       /* We kept the DAR and the height is nearest to the original height */
2005       if (set_w == w) {
2006         gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
2007             G_TYPE_INT, set_h, NULL);
2008         goto done;
2009       }
2010
2011       f_h = set_h;
2012       f_w = set_w;
2013
2014       /* If the former failed, try to keep the input width at least */
2015       tmp = gst_structure_copy (outs);
2016       gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
2017       gst_structure_get_int (tmp, "width", &set_w);
2018
2019       /* This might have failed but try to scale the width
2020        * to keep the DAR nonetheless */
2021       h = (guint) gst_util_uint64_scale_int_round (set_w, den, num);
2022       gst_structure_fixate_field_nearest_int (tmp, "height", h);
2023       gst_structure_get_int (tmp, "height", &set_h);
2024       gst_structure_free (tmp);
2025
2026       /* We kept the DAR and the width is nearest to the original width */
2027       if (set_h == h) {
2028         gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
2029             G_TYPE_INT, set_h, NULL);
2030         goto done;
2031       }
2032
2033       /* If all this failed, keep the dimensions with the DAR that was closest
2034        * to the correct DAR. This changes the DAR but there's not much else to
2035        * do here.
2036        */
2037       if (set_w * ABS (set_h - h) < ABS (f_w - w) * f_h) {
2038         f_h = set_h;
2039         f_w = set_w;
2040       }
2041       gst_structure_set (outs, "width", G_TYPE_INT, f_w, "height", G_TYPE_INT,
2042           f_h, NULL);
2043       goto done;
2044     } else {
2045       GstStructure *tmp;
2046       gint set_h, set_w, set_par_n, set_par_d, tmp2;
2047
2048       /* width, height and PAR are not fixed but passthrough is not possible */
2049
2050       /* First try to keep the height and width as good as possible
2051        * and scale PAR */
2052       tmp = gst_structure_copy (outs);
2053       gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
2054       gst_structure_get_int (tmp, "height", &set_h);
2055       gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
2056       gst_structure_get_int (tmp, "width", &set_w);
2057
2058       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_h, set_w,
2059               &to_par_n, &to_par_d)) {
2060         GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
2061             ("Error calculating the output scaled size - integer overflow"));
2062         gst_structure_free (tmp);
2063         goto done;
2064       }
2065
2066       if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
2067         gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
2068       gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
2069           to_par_n, to_par_d);
2070       gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
2071           &set_par_d);
2072       gst_structure_free (tmp);
2073
2074       if (set_par_n == to_par_n && set_par_d == to_par_d) {
2075         gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
2076             G_TYPE_INT, set_h, NULL);
2077
2078         if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
2079             set_par_n != set_par_d)
2080           gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
2081               set_par_n, set_par_d, NULL);
2082         goto done;
2083       }
2084
2085       /* Otherwise try to scale width to keep the DAR with the set
2086        * PAR and height */
2087       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
2088               set_par_n, &num, &den)) {
2089         GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
2090             ("Error calculating the output scaled size - integer overflow"));
2091         goto done;
2092       }
2093
2094       w = (guint) gst_util_uint64_scale_int_round (set_h, num, den);
2095       tmp = gst_structure_copy (outs);
2096       gst_structure_fixate_field_nearest_int (tmp, "width", w);
2097       gst_structure_get_int (tmp, "width", &tmp2);
2098       gst_structure_free (tmp);
2099
2100       if (tmp2 == w) {
2101         gst_structure_set (outs, "width", G_TYPE_INT, tmp2, "height",
2102             G_TYPE_INT, set_h, NULL);
2103         if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
2104             set_par_n != set_par_d)
2105           gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
2106               set_par_n, set_par_d, NULL);
2107         goto done;
2108       }
2109
2110       /* ... or try the same with the height */
2111       h = (guint) gst_util_uint64_scale_int_round (set_w, den, num);
2112       tmp = gst_structure_copy (outs);
2113       gst_structure_fixate_field_nearest_int (tmp, "height", h);
2114       gst_structure_get_int (tmp, "height", &tmp2);
2115       gst_structure_free (tmp);
2116
2117       if (tmp2 == h) {
2118         gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
2119             G_TYPE_INT, tmp2, NULL);
2120         if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
2121             set_par_n != set_par_d)
2122           gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
2123               set_par_n, set_par_d, NULL);
2124         goto done;
2125       }
2126
2127       /* If all fails we can't keep the DAR and take the nearest values
2128        * for everything from the first try */
2129       gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
2130           G_TYPE_INT, set_h, NULL);
2131       if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
2132           set_par_n != set_par_d)
2133         gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
2134             set_par_n, set_par_d, NULL);
2135     }
2136   }
2137
2138 done:
2139   if (from_par == &fpar)
2140     g_value_unset (&fpar);
2141   if (to_par == &tpar)
2142     g_value_unset (&tpar);
2143
2144   return othercaps;
2145 }
2146
2147 static GstCaps *
2148 gst_va_vpp_fixate_caps (GstBaseTransform * trans, GstPadDirection direction,
2149     GstCaps * caps, GstCaps * othercaps)
2150 {
2151   GstVaVpp *self = GST_VA_VPP (trans);
2152   GstCaps *format;
2153
2154   GST_DEBUG_OBJECT (self,
2155       "trying to fixate othercaps %" GST_PTR_FORMAT " based on caps %"
2156       GST_PTR_FORMAT, othercaps, caps);
2157
2158   format = gst_va_vpp_get_fixed_format (self, direction, caps, othercaps);
2159
2160   if (gst_caps_is_empty (format)) {
2161     GST_ERROR_OBJECT (self, "Could not convert formats");
2162     return format;
2163   }
2164
2165   othercaps = gst_va_vpp_fixate_size (self, direction, caps, othercaps);
2166   if (gst_caps_get_size (othercaps) == 1) {
2167     gint i;
2168     const gchar *format_fields[] = { "format", "colorimetry", "chroma-site" };
2169     GstStructure *format_struct = gst_caps_get_structure (format, 0);
2170     GstStructure *fixated_struct;
2171
2172     othercaps = gst_caps_make_writable (othercaps);
2173     fixated_struct = gst_caps_get_structure (othercaps, 0);
2174
2175     for (i = 0; i < G_N_ELEMENTS (format_fields); i++) {
2176       if (gst_structure_has_field (format_struct, format_fields[i])) {
2177         gst_structure_set (fixated_struct, format_fields[i], G_TYPE_STRING,
2178             gst_structure_get_string (format_struct, format_fields[i]), NULL);
2179       } else {
2180         gst_structure_remove_field (fixated_struct, format_fields[i]);
2181       }
2182     }
2183
2184     /* copy the framerate */
2185     {
2186       const GValue *framerate =
2187           gst_structure_get_value (fixated_struct, "framerate");
2188       if (framerate && !gst_value_is_fixed (framerate)) {
2189         GstStructure *st = gst_caps_get_structure (caps, 0);
2190         const GValue *fixated_framerate =
2191             gst_structure_get_value (st, "framerate");
2192         gst_structure_set_value (fixated_struct, "framerate",
2193             fixated_framerate);
2194       }
2195     }
2196   }
2197   gst_caps_unref (format);
2198
2199   GST_DEBUG_OBJECT (self, "fixated othercaps to %" GST_PTR_FORMAT, othercaps);
2200
2201   return othercaps;
2202 }
2203
2204 static void
2205 _get_scale_factor (GstVaVpp * self, gdouble * w_factor, gdouble * h_factor)
2206 {
2207   gdouble w = GST_VIDEO_INFO_WIDTH (&self->in_info);
2208   gdouble h = GST_VIDEO_INFO_HEIGHT (&self->in_info);
2209
2210   switch (self->direction) {
2211     case GST_VIDEO_ORIENTATION_90R:
2212     case GST_VIDEO_ORIENTATION_90L:
2213     case GST_VIDEO_ORIENTATION_UR_LL:
2214     case GST_VIDEO_ORIENTATION_UL_LR:{
2215       gdouble tmp = h;
2216       h = w;
2217       w = tmp;
2218       break;
2219     }
2220     default:
2221       break;
2222   }
2223
2224   /* TODO: add cropping factor */
2225   *w_factor = GST_VIDEO_INFO_WIDTH (&self->out_info);
2226   *w_factor /= w;
2227
2228   *h_factor = GST_VIDEO_INFO_HEIGHT (&self->out_info);
2229   *h_factor /= h;
2230 }
2231
2232 static gboolean
2233 gst_va_vpp_src_event (GstBaseTransform * trans, GstEvent * event)
2234 {
2235   GstVaVpp *self = GST_VA_VPP (trans);
2236   GstStructure *structure;
2237   const GstVideoInfo *in_info = &self->in_info, *out_info = &self->out_info;
2238   gdouble new_x = 0, new_y = 0, x = 0, y = 0, w_factor = 1, h_factor = 1;
2239   gboolean ret;
2240
2241   GST_TRACE_OBJECT (self, "handling %s event", GST_EVENT_TYPE_NAME (event));
2242
2243   switch (GST_EVENT_TYPE (event)) {
2244     case GST_EVENT_NAVIGATION:
2245       if (GST_VIDEO_INFO_WIDTH (in_info) != GST_VIDEO_INFO_WIDTH (out_info)
2246           || GST_VIDEO_INFO_HEIGHT (in_info) != GST_VIDEO_INFO_HEIGHT (out_info)
2247           || gst_va_filter_get_orientation (self->filter) !=
2248           GST_VIDEO_ORIENTATION_IDENTITY) {
2249
2250         event =
2251             GST_EVENT (gst_mini_object_make_writable (GST_MINI_OBJECT (event)));
2252
2253         structure = (GstStructure *) gst_event_get_structure (event);
2254         if (!gst_structure_get_double (structure, "pointer_x", &x)
2255             || !gst_structure_get_double (structure, "pointer_y", &y))
2256           break;
2257
2258         /* video-direction compensation */
2259         switch (self->direction) {
2260           case GST_VIDEO_ORIENTATION_90R:
2261             new_x = y;
2262             new_y = GST_VIDEO_INFO_WIDTH (in_info) - 1 - x;
2263             break;
2264           case GST_VIDEO_ORIENTATION_90L:
2265             new_x = GST_VIDEO_INFO_HEIGHT (in_info) - 1 - y;
2266             new_y = x;
2267             break;
2268           case GST_VIDEO_ORIENTATION_UR_LL:
2269             new_x = GST_VIDEO_INFO_HEIGHT (in_info) - 1 - y;
2270             new_y = GST_VIDEO_INFO_WIDTH (in_info) - 1 - x;
2271             break;
2272           case GST_VIDEO_ORIENTATION_UL_LR:
2273             new_x = y;
2274             new_y = x;
2275             break;
2276           case GST_VIDEO_ORIENTATION_180:
2277             /* FIXME: is this correct? */
2278             new_x = GST_VIDEO_INFO_WIDTH (in_info) - 1 - x;
2279             new_y = GST_VIDEO_INFO_HEIGHT (in_info) - 1 - y;
2280             break;
2281           case GST_VIDEO_ORIENTATION_HORIZ:
2282             new_x = GST_VIDEO_INFO_WIDTH (in_info) - 1 - x;
2283             new_y = y;
2284             break;
2285           case GST_VIDEO_ORIENTATION_VERT:
2286             new_x = x;
2287             new_y = GST_VIDEO_INFO_HEIGHT (in_info) - 1 - y;
2288             break;
2289           default:
2290             new_x = x;
2291             new_y = y;
2292             break;
2293         }
2294
2295         /* scale compensation */
2296         _get_scale_factor (self, &w_factor, &h_factor);
2297         new_x *= w_factor;
2298         new_y *= h_factor;
2299
2300         /* TODO: crop compensation */
2301
2302         GST_TRACE_OBJECT (self, "from %fx%f to %fx%f", x, y, new_x, new_y);
2303         gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, new_x,
2304             "pointer_y", G_TYPE_DOUBLE, new_y, NULL);
2305       }
2306       break;
2307     default:
2308       break;
2309   }
2310
2311   ret = GST_BASE_TRANSFORM_CLASS (parent_class)->src_event (trans, event);
2312
2313   return ret;
2314 }
2315
2316 static gboolean
2317 gst_va_vpp_sink_event (GstBaseTransform * trans, GstEvent * event)
2318 {
2319   GstVaVpp *self = GST_VA_VPP (trans);
2320   GstTagList *taglist;
2321   gchar *orientation;
2322
2323   switch (GST_EVENT_TYPE (event)) {
2324     case GST_EVENT_TAG:
2325       gst_event_parse_tag (event, &taglist);
2326
2327       if (!gst_tag_list_get_string (taglist, "image-orientation", &orientation))
2328         break;
2329
2330       if (self->direction != GST_VIDEO_ORIENTATION_AUTO)
2331         break;
2332
2333       GST_DEBUG_OBJECT (self, "tag orientation %s", orientation);
2334
2335       GST_OBJECT_LOCK (self);
2336       if (!g_strcmp0 ("rotate-0", orientation))
2337         self->tag_direction = GST_VIDEO_ORIENTATION_IDENTITY;
2338       else if (!g_strcmp0 ("rotate-90", orientation))
2339         self->tag_direction = GST_VIDEO_ORIENTATION_90R;
2340       else if (!g_strcmp0 ("rotate-180", orientation))
2341         self->tag_direction = GST_VIDEO_ORIENTATION_180;
2342       else if (!g_strcmp0 ("rotate-270", orientation))
2343         self->tag_direction = GST_VIDEO_ORIENTATION_90L;
2344       else if (!g_strcmp0 ("flip-rotate-0", orientation))
2345         self->tag_direction = GST_VIDEO_ORIENTATION_HORIZ;
2346       else if (!g_strcmp0 ("flip-rotate-90", orientation))
2347         self->tag_direction = GST_VIDEO_ORIENTATION_UL_LR;
2348       else if (!g_strcmp0 ("flip-rotate-180", orientation))
2349         self->tag_direction = GST_VIDEO_ORIENTATION_VERT;
2350       else if (!g_strcmp0 ("flip-rotate-270", orientation))
2351         self->tag_direction = GST_VIDEO_ORIENTATION_UR_LL;
2352
2353       _update_properties_unlocked (self);
2354       GST_OBJECT_UNLOCK (self);
2355
2356       /* no reconfig here because it's done in
2357        * _update_properties_unlocked */
2358       gst_va_vpp_update_passthrough (self, FALSE);
2359
2360       break;
2361     default:
2362       break;
2363   }
2364
2365   return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event);
2366 }
2367
2368 static GstFlowReturn
2369 gst_va_vpp_generate_output (GstBaseTransform * trans, GstBuffer ** outbuf)
2370 {
2371   GstVaVpp *self = GST_VA_VPP (trans);
2372   GstVideoFrame src_frame;
2373   GstVideoFrame dest_frame;
2374   GstBuffer *buffer = NULL;
2375   GstFlowReturn ret;
2376
2377   ret = GST_BASE_TRANSFORM_CLASS (parent_class)->generate_output (trans,
2378       outbuf);
2379
2380   if (ret != GST_FLOW_OK || *outbuf == NULL)
2381     return ret;
2382
2383   if (!self->other_pool)
2384     return GST_FLOW_OK;
2385
2386   /* Now need to copy the output buffer */
2387   ret = GST_FLOW_ERROR;
2388
2389   if (!gst_buffer_pool_set_active (self->other_pool, TRUE)) {
2390     GST_WARNING_OBJECT (self, "failed to active the other pool %"
2391         GST_PTR_FORMAT, self->other_pool);
2392     goto out;
2393   }
2394
2395   ret = gst_buffer_pool_acquire_buffer (self->other_pool, &buffer, NULL);
2396   if (ret != GST_FLOW_OK)
2397     goto out;
2398
2399   if (!gst_video_frame_map (&src_frame, &self->srcpad_info, *outbuf,
2400           GST_MAP_READ))
2401     goto out;
2402
2403   if (!gst_video_frame_map (&dest_frame, &self->out_info, buffer,
2404           GST_MAP_WRITE)) {
2405     gst_video_frame_unmap (&src_frame);
2406     goto out;
2407   }
2408
2409   if (!gst_video_frame_copy (&dest_frame, &src_frame)) {
2410     gst_video_frame_unmap (&src_frame);
2411     gst_video_frame_unmap (&dest_frame);
2412     goto out;
2413   }
2414
2415   gst_video_frame_unmap (&src_frame);
2416   gst_video_frame_unmap (&dest_frame);
2417
2418   gst_buffer_replace (outbuf, buffer);
2419   ret = GST_FLOW_OK;
2420
2421 out:
2422   gst_clear_buffer (&buffer);
2423   return ret;
2424 }
2425
2426 static void
2427 gst_va_vpp_class_init (gpointer g_class, gpointer class_data)
2428 {
2429   GstCaps *doc_caps, *caps = NULL;
2430   GstPadTemplate *sink_pad_templ, *src_pad_templ;
2431   GObjectClass *object_class = G_OBJECT_CLASS (g_class);
2432   GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (g_class);
2433   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
2434   GstVaDisplay *display;
2435   GstVaFilter *filter;
2436   GstVaVppClass *klass = GST_VA_VPP_CLASS (g_class);
2437   struct CData *cdata = class_data;
2438   gchar *long_name;
2439
2440   parent_class = g_type_class_peek_parent (g_class);
2441
2442   klass->render_device_path = g_strdup (cdata->render_device_path);
2443
2444   if (cdata->description) {
2445     long_name = g_strdup_printf ("VA-API Video Postprocessor in %s",
2446         cdata->description);
2447   } else {
2448     long_name = g_strdup ("VA-API Video Postprocessor");
2449   }
2450
2451   gst_element_class_set_metadata (element_class, long_name,
2452       "Filter/Converter/Video/Scaler/Hardware",
2453       "VA-API based video postprocessor",
2454       "Víctor Jáquez <vjaquez@igalia.com>");
2455
2456   display = gst_va_display_drm_new_from_path (klass->render_device_path);
2457   filter = gst_va_filter_new (display);
2458
2459   if (gst_va_filter_open (filter))
2460     caps = gst_va_filter_get_caps (filter);
2461   else
2462     caps = gst_caps_from_string (caps_str);
2463
2464   doc_caps = gst_caps_from_string (caps_str);
2465
2466   sink_pad_templ = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
2467       caps);
2468   gst_element_class_add_pad_template (element_class, sink_pad_templ);
2469   gst_pad_template_set_documentation_caps (sink_pad_templ,
2470       gst_caps_ref (doc_caps));
2471
2472   src_pad_templ = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
2473       caps);
2474   gst_element_class_add_pad_template (element_class, src_pad_templ);
2475   gst_pad_template_set_documentation_caps (src_pad_templ,
2476       gst_caps_ref (doc_caps));
2477   gst_caps_unref (doc_caps);
2478
2479   gst_caps_unref (caps);
2480
2481   object_class->dispose = gst_va_vpp_dispose;
2482   object_class->set_property = gst_va_vpp_set_property;
2483   object_class->get_property = gst_va_vpp_get_property;
2484
2485   element_class->change_state = GST_DEBUG_FUNCPTR (gst_va_vpp_change_state);
2486   element_class->set_context = GST_DEBUG_FUNCPTR (gst_va_vpp_set_context);
2487
2488   trans_class->propose_allocation =
2489       GST_DEBUG_FUNCPTR (gst_va_vpp_propose_allocation);
2490   trans_class->decide_allocation =
2491       GST_DEBUG_FUNCPTR (gst_va_vpp_decide_allocation);
2492   trans_class->query = GST_DEBUG_FUNCPTR (gst_va_vpp_query);
2493   trans_class->transform_caps = GST_DEBUG_FUNCPTR (gst_va_vpp_transform_caps);
2494   trans_class->fixate_caps = GST_DEBUG_FUNCPTR (gst_va_vpp_fixate_caps);
2495   trans_class->transform_size = GST_DEBUG_FUNCPTR (gst_va_vpp_transform_size);
2496   trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_va_vpp_set_caps);
2497   trans_class->before_transform =
2498       GST_DEBUG_FUNCPTR (gst_va_vpp_before_transform);
2499   trans_class->transform = GST_DEBUG_FUNCPTR (gst_va_vpp_transform);
2500   trans_class->transform_meta = GST_DEBUG_FUNCPTR (gst_va_vpp_transform_meta);
2501   trans_class->src_event = GST_DEBUG_FUNCPTR (gst_va_vpp_src_event);
2502   trans_class->sink_event = GST_DEBUG_FUNCPTR (gst_va_vpp_sink_event);
2503   trans_class->generate_output = GST_DEBUG_FUNCPTR (gst_va_vpp_generate_output);
2504
2505   trans_class->transform_ip_on_passthrough = FALSE;
2506
2507   gst_va_filter_install_properties (filter, object_class);
2508
2509   g_free (long_name);
2510   g_free (cdata->description);
2511   g_free (cdata->render_device_path);
2512   g_free (cdata);
2513   gst_object_unref (filter);
2514   gst_object_unref (display);
2515 }
2516
2517 static inline void
2518 _create_colorbalance_channel (GstVaVpp * self, const gchar * label)
2519 {
2520   GstColorBalanceChannel *channel;
2521
2522   channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
2523   channel->label = g_strdup_printf ("VA-%s", label);
2524   channel->min_value = -1000;
2525   channel->max_value = 1000;
2526
2527   self->channels = g_list_append (self->channels, channel);
2528 }
2529
2530 static void
2531 gst_va_vpp_init (GTypeInstance * instance, gpointer g_class)
2532 {
2533   GstVaVpp *self = GST_VA_VPP (instance);
2534   GParamSpec *pspec;
2535
2536   self->direction = GST_VIDEO_ORIENTATION_IDENTITY;
2537   self->prev_direction = self->direction;
2538   self->tag_direction = GST_VIDEO_ORIENTATION_AUTO;
2539
2540   pspec = g_object_class_find_property (g_class, "denoise");
2541   if (pspec)
2542     self->denoise = g_value_get_float (g_param_spec_get_default_value (pspec));
2543
2544   pspec = g_object_class_find_property (g_class, "sharpen");
2545   if (pspec)
2546     self->sharpen = g_value_get_float (g_param_spec_get_default_value (pspec));
2547
2548   pspec = g_object_class_find_property (g_class, "skin-tone");
2549   if (pspec) {
2550     const GValue *value = g_param_spec_get_default_value (pspec);
2551     if (G_VALUE_TYPE (value) == G_TYPE_BOOLEAN)
2552       self->skintone = g_value_get_boolean (value);
2553     else
2554       self->skintone = g_value_get_float (value);
2555   }
2556
2557   /* color balance */
2558   pspec = g_object_class_find_property (g_class, "brightness");
2559   if (pspec) {
2560     self->brightness =
2561         g_value_get_float (g_param_spec_get_default_value (pspec));
2562     _create_colorbalance_channel (self, "BRIGHTNESS");
2563   }
2564   pspec = g_object_class_find_property (g_class, "contrast");
2565   if (pspec) {
2566     self->contrast = g_value_get_float (g_param_spec_get_default_value (pspec));
2567     _create_colorbalance_channel (self, "CONTRAST");
2568   }
2569   pspec = g_object_class_find_property (g_class, "hue");
2570   if (pspec) {
2571     self->hue = g_value_get_float (g_param_spec_get_default_value (pspec));
2572     _create_colorbalance_channel (self, "HUE");
2573   }
2574   pspec = g_object_class_find_property (g_class, "saturation");
2575   if (pspec) {
2576     self->saturation =
2577         g_value_get_float (g_param_spec_get_default_value (pspec));
2578     _create_colorbalance_channel (self, "SATURATION");
2579   }
2580
2581   /* enable QoS */
2582   gst_base_transform_set_qos_enabled (GST_BASE_TRANSFORM (instance), TRUE);
2583 }
2584
2585 static gpointer
2586 _register_debug_category (gpointer data)
2587 {
2588   GST_DEBUG_CATEGORY_INIT (gst_va_vpp_debug, "vavpp", 0,
2589       "VA Video Postprocessor");
2590
2591 #define D(type) \
2592   G_PASTE (META_TAG_, type) = \
2593     g_quark_from_static_string (G_PASTE (G_PASTE (GST_META_TAG_VIDEO_, type), _STR))
2594   D (COLORSPACE);
2595   D (SIZE);
2596   D (ORIENTATION);
2597 #undef D
2598   META_TAG_VIDEO = g_quark_from_static_string (GST_META_TAG_VIDEO_STR);
2599
2600   return NULL;
2601 }
2602
2603 gboolean
2604 gst_va_vpp_register (GstPlugin * plugin, GstVaDevice * device, guint rank)
2605 {
2606   static GOnce debug_once = G_ONCE_INIT;
2607   GType type;
2608   GTypeInfo type_info = {
2609     .class_size = sizeof (GstVaVppClass),
2610     .class_init = gst_va_vpp_class_init,
2611     .instance_size = sizeof (GstVaVpp),
2612     .instance_init = gst_va_vpp_init,
2613   };
2614   struct CData *cdata;
2615   gboolean ret;
2616   gchar *type_name, *feature_name;
2617
2618   g_return_val_if_fail (GST_IS_PLUGIN (plugin), FALSE);
2619   g_return_val_if_fail (GST_IS_VA_DEVICE (device), FALSE);
2620
2621   cdata = g_new (struct CData, 1);
2622   cdata->description = NULL;
2623   cdata->render_device_path = g_strdup (device->render_device_path);
2624
2625   type_info.class_data = cdata;
2626
2627   type_name = g_strdup ("GstVaPostProc");
2628   feature_name = g_strdup ("vapostproc");
2629
2630   /* The first postprocessor to be registered should use a constant
2631    * name, like vapostproc, for any additional postprocessors, we
2632    * create unique names, using inserting the render device name. */
2633   if (g_type_from_name (type_name)) {
2634     gchar *basename = g_path_get_basename (device->render_device_path);
2635     g_free (type_name);
2636     g_free (feature_name);
2637     type_name = g_strdup_printf ("GstVa%sPostProc", basename);
2638     feature_name = g_strdup_printf ("va%spostproc", basename);
2639     cdata->description = basename;
2640
2641     /* lower rank for non-first device */
2642     if (rank > 0)
2643       rank--;
2644   }
2645
2646   g_once (&debug_once, _register_debug_category, NULL);
2647
2648   type = g_type_register_static (GST_TYPE_BASE_TRANSFORM, type_name, &type_info,
2649       0);
2650
2651   {
2652     GstVaFilter *filter = gst_va_filter_new (device->display);
2653     if (gst_va_filter_open (filter)
2654         && gst_va_filter_has_filter (filter, VAProcFilterColorBalance)) {
2655       const GInterfaceInfo info = { gst_va_vpp_colorbalance_init, NULL, NULL };
2656       g_type_add_interface_static (type, GST_TYPE_COLOR_BALANCE, &info);
2657     }
2658     gst_object_unref (filter);
2659   }
2660
2661   ret = gst_element_register (plugin, feature_name, rank, type);
2662
2663   g_free (type_name);
2664   g_free (feature_name);
2665
2666   return ret;
2667 }
2668
2669 /* Color Balance interface */
2670 static const GList *
2671 gst_va_vpp_colorbalance_list_channels (GstColorBalance * balance)
2672 {
2673   GstVaVpp *self = GST_VA_VPP (balance);
2674
2675   return self->channels;
2676 }
2677
2678 static gboolean
2679 _set_cb_val (GstVaVpp * self, const gchar * name,
2680     GstColorBalanceChannel * channel, gint value, gfloat * cb)
2681 {
2682   GObjectClass *klass = G_OBJECT_CLASS (GST_VA_VPP_GET_CLASS (self));
2683   GParamSpec *pspec;
2684   GParamSpecFloat *fpspec;
2685   gfloat new_value;
2686   gboolean changed;
2687
2688   pspec = g_object_class_find_property (klass, name);
2689   if (!pspec)
2690     return FALSE;
2691
2692   fpspec = G_PARAM_SPEC_FLOAT (pspec);
2693   new_value = (value - channel->min_value) * (fpspec->maximum - fpspec->minimum)
2694       / (channel->max_value - channel->min_value) + fpspec->minimum;
2695
2696   GST_OBJECT_LOCK (self);
2697   changed = new_value != *cb;
2698   *cb = new_value;
2699   value = (*cb + fpspec->minimum) * (channel->max_value - channel->min_value)
2700       / (fpspec->maximum - fpspec->minimum) + channel->min_value;
2701   GST_OBJECT_UNLOCK (self);
2702
2703   if (changed) {
2704     GST_INFO_OBJECT (self, "%s: %d / %f", channel->label, value, new_value);
2705     gst_color_balance_value_changed (GST_COLOR_BALANCE (self), channel, value);
2706     g_atomic_int_set (&self->rebuild_filters, TRUE);
2707   }
2708
2709   return TRUE;
2710 }
2711
2712 static void
2713 gst_va_vpp_colorbalance_set_value (GstColorBalance * balance,
2714     GstColorBalanceChannel * channel, gint value)
2715 {
2716   GstVaVpp *self = GST_VA_VPP (balance);
2717
2718   if (g_str_has_suffix (channel->label, "HUE"))
2719     _set_cb_val (self, "hue", channel, value, &self->hue);
2720   else if (g_str_has_suffix (channel->label, "BRIGHTNESS"))
2721     _set_cb_val (self, "brightness", channel, value, &self->brightness);
2722   else if (g_str_has_suffix (channel->label, "CONTRAST"))
2723     _set_cb_val (self, "contrast", channel, value, &self->contrast);
2724   else if (g_str_has_suffix (channel->label, "SATURATION"))
2725     _set_cb_val (self, "saturation", channel, value, &self->saturation);
2726 }
2727
2728 static gboolean
2729 _get_cb_val (GstVaVpp * self, const gchar * name,
2730     GstColorBalanceChannel * channel, gfloat * cb, gint * val)
2731 {
2732   GObjectClass *klass = G_OBJECT_CLASS (GST_VA_VPP_GET_CLASS (self));
2733   GParamSpec *pspec;
2734   GParamSpecFloat *fpspec;
2735
2736   pspec = g_object_class_find_property (klass, name);
2737   if (!pspec)
2738     return FALSE;
2739
2740   fpspec = G_PARAM_SPEC_FLOAT (pspec);
2741
2742   GST_OBJECT_LOCK (self);
2743   *val = (*cb + fpspec->minimum) * (channel->max_value - channel->min_value)
2744       / (fpspec->maximum - fpspec->minimum) + channel->min_value;
2745   GST_OBJECT_UNLOCK (self);
2746
2747   return TRUE;
2748 }
2749
2750 static gint
2751 gst_va_vpp_colorbalance_get_value (GstColorBalance * balance,
2752     GstColorBalanceChannel * channel)
2753 {
2754   GstVaVpp *self = GST_VA_VPP (balance);
2755   gint value = 0;
2756
2757   if (g_str_has_suffix (channel->label, "HUE"))
2758     _get_cb_val (self, "hue", channel, &self->hue, &value);
2759   else if (g_str_has_suffix (channel->label, "BRIGHTNESS"))
2760     _get_cb_val (self, "brightness", channel, &self->brightness, &value);
2761   else if (g_str_has_suffix (channel->label, "CONTRAST"))
2762     _get_cb_val (self, "contrast", channel, &self->contrast, &value);
2763   else if (g_str_has_suffix (channel->label, "SATURATION"))
2764     _get_cb_val (self, "saturation", channel, &self->saturation, &value);
2765
2766   return value;
2767 }
2768
2769 static GstColorBalanceType
2770 gst_va_vpp_colorbalance_get_balance_type (GstColorBalance * balance)
2771 {
2772   return GST_COLOR_BALANCE_HARDWARE;
2773 }
2774
2775 static void
2776 gst_va_vpp_colorbalance_init (gpointer iface, gpointer data)
2777 {
2778   GstColorBalanceInterface *cbiface = iface;
2779
2780   cbiface->list_channels = gst_va_vpp_colorbalance_list_channels;
2781   cbiface->set_value = gst_va_vpp_colorbalance_set_value;
2782   cbiface->get_value = gst_va_vpp_colorbalance_get_value;
2783   cbiface->get_balance_type = gst_va_vpp_colorbalance_get_balance_type;
2784 }