vaapipostproc: advertise crop meta is handled
[platform/upstream/gstreamer.git] / gst / vaapi / gstvaapipostproc.c
1 /*
2  *  gstvaapipostproc.c - VA-API video postprocessing
3  *
4  *  Copyright (C) 2012-2014 Intel Corporation
5  *    Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Lesser General Public License
9  *  as published by the Free Software Foundation; either version 2.1
10  *  of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this library; if not, write to the Free
19  *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  *  Boston, MA 02110-1301 USA
21  */
22
23 /**
24  * SECTION:element-vaapipostproc
25  * @short_description: A VA-API base video postprocessing filter
26  *
27  * vaapipostproc consists in various postprocessing algorithms to be
28  * applied to VA surfaces.
29  *
30  * ## Example launch line
31  *
32  * |[
33  * gst-launch-1.0 videotestsrc ! vaapipostproc ! video/x-raw width=1920, height=1080 ! vaapisink
34  * ]|
35  */
36
37 #include "gstcompat.h"
38 #include <gst/video/video.h>
39
40 #include <gst/vaapi/gstvaapivalue.h>
41
42 #include "gstvaapipostproc.h"
43 #include "gstvaapipostprocutil.h"
44 #include "gstvaapipluginutil.h"
45 #include "gstvaapivideobuffer.h"
46 #include "gstvaapivideobufferpool.h"
47 #include "gstvaapivideomemory.h"
48
49 #define GST_PLUGIN_NAME "vaapipostproc"
50 #define GST_PLUGIN_DESC "A VA-API video postprocessing filter"
51
52 GST_DEBUG_CATEGORY_STATIC (gst_debug_vaapipostproc);
53 #ifndef GST_DISABLE_GST_DEBUG
54 #define GST_CAT_DEFAULT gst_debug_vaapipostproc
55 #else
56 #define GST_CAT_DEFAULT NULL
57 #endif
58
59 /* Default templates */
60 /* *INDENT-OFF* */
61 static const char gst_vaapipostproc_sink_caps_str[] =
62   GST_VAAPI_MAKE_SURFACE_CAPS ", "
63   GST_CAPS_INTERLACED_MODES "; "
64   GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL) ", "
65    GST_CAPS_INTERLACED_MODES;
66 /* *INDENT-ON* */
67
68 /* *INDENT-OFF* */
69 static const char gst_vaapipostproc_src_caps_str[] =
70   GST_VAAPI_MAKE_SURFACE_CAPS ", "
71   GST_CAPS_INTERLACED_FALSE "; "
72 #if (USE_GLX || USE_EGL)
73   GST_VAAPI_MAKE_GLTEXUPLOAD_CAPS "; "
74 #endif
75   GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL) ", "
76   GST_CAPS_INTERLACED_MODES "; "
77   GST_VAAPI_MAKE_DMABUF_CAPS;
78 /* *INDENT-ON* */
79
80 /* *INDENT-OFF* */
81 static GstStaticPadTemplate gst_vaapipostproc_sink_factory =
82   GST_STATIC_PAD_TEMPLATE ("sink",
83     GST_PAD_SINK,
84     GST_PAD_ALWAYS,
85     GST_STATIC_CAPS (gst_vaapipostproc_sink_caps_str));
86 /* *INDENT-ON* */
87
88 /* *INDENT-OFF* */
89 static GstStaticPadTemplate gst_vaapipostproc_src_factory =
90   GST_STATIC_PAD_TEMPLATE ("src",
91     GST_PAD_SRC,
92     GST_PAD_ALWAYS,
93     GST_STATIC_CAPS (gst_vaapipostproc_src_caps_str));
94 /* *INDENT-ON* */
95
96 static void gst_vaapipostproc_colorbalance_init (gpointer iface, gpointer data);
97
98 G_DEFINE_TYPE_WITH_CODE (GstVaapiPostproc, gst_vaapipostproc,
99     GST_TYPE_BASE_TRANSFORM, GST_VAAPI_PLUGIN_BASE_INIT_INTERFACES
100     G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE,
101         gst_vaapipostproc_colorbalance_init));
102
103 GST_VAAPI_PLUGIN_BASE_DEFINE_SET_CONTEXT (gst_vaapipostproc_parent_class);
104
105 static GstVideoFormat native_formats[] =
106     { GST_VIDEO_FORMAT_NV12, GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_I420 };
107
108 enum
109 {
110   PROP_0,
111
112   PROP_FORMAT,
113   PROP_WIDTH,
114   PROP_HEIGHT,
115   PROP_FORCE_ASPECT_RATIO,
116   PROP_DEINTERLACE_MODE,
117   PROP_DEINTERLACE_METHOD,
118   PROP_DENOISE,
119   PROP_SHARPEN,
120   PROP_HUE,
121   PROP_SATURATION,
122   PROP_BRIGHTNESS,
123   PROP_CONTRAST,
124   PROP_SCALE_METHOD,
125   PROP_VIDEO_DIRECTION,
126   PROP_SKIN_TONE_ENHANCEMENT,
127 };
128
129 #define GST_VAAPI_TYPE_DEINTERLACE_MODE \
130     gst_vaapi_deinterlace_mode_get_type()
131
132 static GType
133 gst_vaapi_deinterlace_mode_get_type (void)
134 {
135   static GType deinterlace_mode_type = 0;
136
137   static const GEnumValue mode_types[] = {
138     {GST_VAAPI_DEINTERLACE_MODE_AUTO,
139         "Auto detection", "auto"},
140     {GST_VAAPI_DEINTERLACE_MODE_INTERLACED,
141         "Force deinterlacing", "interlaced"},
142     {GST_VAAPI_DEINTERLACE_MODE_DISABLED,
143         "Never deinterlace", "disabled"},
144     {0, NULL, NULL},
145   };
146
147   if (!deinterlace_mode_type) {
148     deinterlace_mode_type =
149         g_enum_register_static ("GstVaapiDeinterlaceMode", mode_types);
150   }
151   return deinterlace_mode_type;
152 }
153
154 static void
155 ds_reset (GstVaapiDeinterlaceState * ds)
156 {
157   guint i;
158
159   for (i = 0; i < G_N_ELEMENTS (ds->buffers); i++)
160     gst_buffer_replace (&ds->buffers[i], NULL);
161   ds->buffers_index = 0;
162   ds->num_surfaces = 0;
163   ds->deint = FALSE;
164   ds->tff = FALSE;
165 }
166
167 static void
168 ds_add_buffer (GstVaapiDeinterlaceState * ds, GstBuffer * buf)
169 {
170   gst_buffer_replace (&ds->buffers[ds->buffers_index], buf);
171   ds->buffers_index = (ds->buffers_index + 1) % G_N_ELEMENTS (ds->buffers);
172 }
173
174 static inline GstBuffer *
175 ds_get_buffer (GstVaapiDeinterlaceState * ds, guint index)
176 {
177   /* Note: the index increases towards older buffers.
178      i.e. buffer at index 0 means the immediately preceding buffer
179      in the history, buffer at index 1 means the one preceding the
180      surface at index 0, etc. */
181   const guint n = ds->buffers_index + G_N_ELEMENTS (ds->buffers) - index - 1;
182   return ds->buffers[n % G_N_ELEMENTS (ds->buffers)];
183 }
184
185 static void
186 ds_set_surfaces (GstVaapiDeinterlaceState * ds)
187 {
188   GstVaapiVideoMeta *meta;
189   guint i;
190
191   ds->num_surfaces = 0;
192   for (i = 0; i < G_N_ELEMENTS (ds->buffers); i++) {
193     GstBuffer *const buf = ds_get_buffer (ds, i);
194     if (!buf)
195       break;
196
197     meta = gst_buffer_get_vaapi_video_meta (buf);
198     ds->surfaces[ds->num_surfaces++] = gst_vaapi_video_meta_get_surface (meta);
199   }
200 }
201
202 static GstVaapiFilterOpInfo *
203 find_filter_op (GPtrArray * filter_ops, GstVaapiFilterOp op)
204 {
205   guint i;
206
207   if (filter_ops) {
208     for (i = 0; i < filter_ops->len; i++) {
209       GstVaapiFilterOpInfo *const filter_op = g_ptr_array_index (filter_ops, i);
210       if (filter_op->op == op)
211         return filter_op;
212     }
213   }
214   return NULL;
215 }
216
217 static inline gboolean
218 gst_vaapipostproc_ensure_display (GstVaapiPostproc * postproc)
219 {
220   return
221       gst_vaapi_plugin_base_ensure_display (GST_VAAPI_PLUGIN_BASE (postproc));
222 }
223
224 static gboolean
225 gst_vaapipostproc_ensure_filter (GstVaapiPostproc * postproc)
226 {
227   if (postproc->filter)
228     return TRUE;
229
230   if (!gst_vaapipostproc_ensure_display (postproc))
231     return FALSE;
232
233   gst_caps_replace (&postproc->allowed_srcpad_caps, NULL);
234   gst_caps_replace (&postproc->allowed_sinkpad_caps, NULL);
235
236   postproc->filter =
237       gst_vaapi_filter_new (GST_VAAPI_PLUGIN_BASE_DISPLAY (postproc));
238   if (!postproc->filter)
239     return FALSE;
240   return TRUE;
241 }
242
243 static gboolean
244 gst_vaapipostproc_ensure_filter_caps (GstVaapiPostproc * postproc)
245 {
246   if (!gst_vaapipostproc_ensure_filter (postproc))
247     return FALSE;
248
249   if (!postproc->filter_ops) {
250     postproc->filter_ops = gst_vaapi_filter_get_operations (postproc->filter);
251     if (!postproc->filter_ops)
252       return FALSE;
253   }
254
255   if (!postproc->filter_formats) {
256     postproc->filter_formats = gst_vaapi_filter_get_formats (postproc->filter);
257     if (!postproc->filter_formats)
258       return FALSE;
259   }
260   return TRUE;
261 }
262
263 static gboolean
264 gst_vaapipostproc_create (GstVaapiPostproc * postproc)
265 {
266   if (!gst_vaapi_plugin_base_open (GST_VAAPI_PLUGIN_BASE (postproc)))
267     return FALSE;
268   if (!gst_vaapipostproc_ensure_display (postproc))
269     return FALSE;
270
271   postproc->use_vpp = FALSE;
272   postproc->has_vpp = gst_vaapipostproc_ensure_filter (postproc);
273   return TRUE;
274 }
275
276 static void
277 gst_vaapipostproc_destroy_filter (GstVaapiPostproc * postproc)
278 {
279   if (postproc->filter_formats) {
280     g_array_unref (postproc->filter_formats);
281     postproc->filter_formats = NULL;
282   }
283
284   if (postproc->filter_ops) {
285     g_ptr_array_unref (postproc->filter_ops);
286     postproc->filter_ops = NULL;
287   }
288   if (postproc->cb_channels) {
289     g_list_free_full (postproc->cb_channels, g_object_unref);
290     postproc->cb_channels = NULL;
291   }
292   gst_vaapi_filter_replace (&postproc->filter, NULL);
293   gst_vaapi_video_pool_replace (&postproc->filter_pool, NULL);
294 }
295
296 static void
297 gst_vaapipostproc_destroy (GstVaapiPostproc * postproc)
298 {
299   ds_reset (&postproc->deinterlace_state);
300   gst_vaapipostproc_destroy_filter (postproc);
301
302   gst_caps_replace (&postproc->allowed_sinkpad_caps, NULL);
303   gst_caps_replace (&postproc->allowed_srcpad_caps, NULL);
304   gst_vaapi_plugin_base_close (GST_VAAPI_PLUGIN_BASE (postproc));
305 }
306
307 static gboolean
308 gst_vaapipostproc_start (GstBaseTransform * trans)
309 {
310   GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
311
312   ds_reset (&postproc->deinterlace_state);
313   if (!gst_vaapi_plugin_base_open (GST_VAAPI_PLUGIN_BASE (postproc)))
314     return FALSE;
315   g_mutex_lock (&postproc->postproc_lock);
316   gst_vaapipostproc_ensure_filter (postproc);
317   g_mutex_unlock (&postproc->postproc_lock);
318
319   return TRUE;
320 }
321
322 static gboolean
323 gst_vaapipostproc_stop (GstBaseTransform * trans)
324 {
325   GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
326
327   g_mutex_lock (&postproc->postproc_lock);
328   ds_reset (&postproc->deinterlace_state);
329   gst_vaapi_plugin_base_close (GST_VAAPI_PLUGIN_BASE (postproc));
330
331   postproc->field_duration = GST_CLOCK_TIME_NONE;
332   gst_video_info_init (&postproc->sinkpad_info);
333   gst_video_info_init (&postproc->srcpad_info);
334   gst_video_info_init (&postproc->filter_pool_info);
335   g_mutex_unlock (&postproc->postproc_lock);
336
337   return TRUE;
338 }
339
340 static gboolean
341 should_deinterlace_buffer (GstVaapiPostproc * postproc, GstBuffer * buf)
342 {
343   if (!(postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) ||
344       postproc->deinterlace_mode == GST_VAAPI_DEINTERLACE_MODE_DISABLED)
345     return FALSE;
346
347   if (postproc->deinterlace_mode == GST_VAAPI_DEINTERLACE_MODE_INTERLACED)
348     return TRUE;
349
350   g_assert (postproc->deinterlace_mode == GST_VAAPI_DEINTERLACE_MODE_AUTO);
351
352   switch (GST_VIDEO_INFO_INTERLACE_MODE (&postproc->sinkpad_info)) {
353     case GST_VIDEO_INTERLACE_MODE_INTERLEAVED:
354       return TRUE;
355     case GST_VIDEO_INTERLACE_MODE_PROGRESSIVE:
356       return FALSE;
357     case GST_VIDEO_INTERLACE_MODE_MIXED:
358       if (GST_BUFFER_FLAG_IS_SET (buf, GST_VIDEO_BUFFER_FLAG_INTERLACED))
359         return TRUE;
360       break;
361     default:
362       GST_ERROR_OBJECT (postproc,
363           "unhandled \"interlace-mode\", disabling deinterlacing");
364       break;
365   }
366   return FALSE;
367 }
368
369 static GstBuffer *
370 create_output_buffer (GstVaapiPostproc * postproc)
371 {
372   GstBuffer *outbuf;
373
374   GstBufferPool *const pool =
375       GST_VAAPI_PLUGIN_BASE (postproc)->srcpad_buffer_pool;
376   GstFlowReturn ret;
377
378   g_return_val_if_fail (pool != NULL, NULL);
379
380   if (!gst_buffer_pool_is_active (pool) &&
381       !gst_buffer_pool_set_active (pool, TRUE))
382     goto error_activate_pool;
383
384   outbuf = NULL;
385   ret = gst_buffer_pool_acquire_buffer (pool, &outbuf, NULL);
386   if (ret != GST_FLOW_OK || !outbuf)
387     goto error_create_buffer;
388   return outbuf;
389
390   /* ERRORS */
391 error_activate_pool:
392   {
393     GST_ERROR_OBJECT (postproc, "failed to activate output video buffer pool");
394     return NULL;
395   }
396 error_create_buffer:
397   {
398     GST_ERROR_OBJECT (postproc, "failed to create output video buffer");
399     return NULL;
400   }
401 }
402
403 static inline GstBuffer *
404 create_output_dump_buffer (GstVaapiPostproc * postproc)
405 {
406   GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (postproc);
407
408   return gst_buffer_new_allocate (plugin->other_srcpad_allocator,
409       GST_VIDEO_INFO_SIZE (&plugin->srcpad_info),
410       &plugin->other_allocator_params);
411 }
412
413 static void
414 copy_metadata (GstVaapiPostproc * postproc, GstBuffer * outbuf,
415     GstBuffer * inbuf)
416 {
417   GstBaseTransformClass *bclass = GST_BASE_TRANSFORM_GET_CLASS (postproc);
418   GstBaseTransform *trans = GST_BASE_TRANSFORM (postproc);
419
420   if (inbuf == outbuf)
421     return;
422   if (!bclass->copy_metadata)
423     return;
424   if (!bclass->copy_metadata (trans, inbuf, outbuf)) {
425     /* something failed, post a warning */
426     GST_ELEMENT_WARNING (trans, STREAM, NOT_IMPLEMENTED,
427         ("could not copy metadata"), (NULL));
428   }
429 }
430
431 static gboolean
432 append_output_buffer_metadata (GstVaapiPostproc * postproc, GstBuffer * outbuf,
433     GstBuffer * inbuf, guint flags)
434 {
435   GstVaapiVideoMeta *inbuf_meta, *outbuf_meta;
436   GstVaapiSurfaceProxy *proxy;
437
438   gst_buffer_copy_into (outbuf, inbuf, flags | GST_BUFFER_COPY_FLAGS, 0, -1);
439
440   copy_metadata (postproc, outbuf, inbuf);
441
442   /* GstVaapiVideoMeta */
443   inbuf_meta = gst_buffer_get_vaapi_video_meta (inbuf);
444   g_return_val_if_fail (inbuf_meta != NULL, FALSE);
445   proxy = gst_vaapi_video_meta_get_surface_proxy (inbuf_meta);
446
447   outbuf_meta = gst_buffer_get_vaapi_video_meta (outbuf);
448   g_return_val_if_fail (outbuf_meta != NULL, FALSE);
449   proxy = gst_vaapi_surface_proxy_copy (proxy);
450   if (!proxy)
451     return FALSE;
452
453   gst_vaapi_video_meta_set_surface_proxy (outbuf_meta, proxy);
454   gst_vaapi_surface_proxy_unref (proxy);
455   return TRUE;
456 }
457
458 static gboolean
459 deint_method_is_advanced (GstVaapiDeinterlaceMethod deint_method)
460 {
461   gboolean is_advanced;
462
463   switch (deint_method) {
464     case GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE:
465     case GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED:
466       is_advanced = TRUE;
467       break;
468     default:
469       is_advanced = FALSE;
470       break;
471   }
472   return is_advanced;
473 }
474
475 static GstVaapiDeinterlaceMethod
476 get_next_deint_method (GstVaapiDeinterlaceMethod deint_method)
477 {
478   switch (deint_method) {
479     case GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED:
480       deint_method = GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE;
481       break;
482     default:
483       /* Default to basic "bob" for all others */
484       deint_method = GST_VAAPI_DEINTERLACE_METHOD_BOB;
485       break;
486   }
487   return deint_method;
488 }
489
490 static gboolean
491 set_best_deint_method (GstVaapiPostproc * postproc, guint flags,
492     GstVaapiDeinterlaceMethod * deint_method_ptr)
493 {
494   GstVaapiDeinterlaceMethod deint_method = postproc->deinterlace_method;
495   gboolean success;
496
497   for (;;) {
498     success = gst_vaapi_filter_set_deinterlacing (postproc->filter,
499         deint_method, flags);
500     if (success || deint_method == GST_VAAPI_DEINTERLACE_METHOD_BOB)
501       break;
502     deint_method = get_next_deint_method (deint_method);
503   }
504   *deint_method_ptr = deint_method;
505   return success;
506 }
507
508 static gboolean
509 check_filter_update (GstVaapiPostproc * postproc)
510 {
511   guint filter_flag = postproc->flags;
512   guint op_flag;
513   gint i;
514
515   if (!postproc->has_vpp)
516     return FALSE;
517
518   for (i = GST_VAAPI_FILTER_OP_DENOISE; i <= GST_VAAPI_FILTER_OP_SKINTONE; i++) {
519     op_flag = (filter_flag >> i) & 1;
520     if (op_flag)
521       return TRUE;
522   }
523
524   return FALSE;
525 }
526
527 static gboolean
528 update_filter (GstVaapiPostproc * postproc)
529 {
530   /* Validate filters */
531   if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_FORMAT) &&
532       !gst_vaapi_filter_set_format (postproc->filter, postproc->format))
533     return FALSE;
534
535   if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_DENOISE) {
536     if (!gst_vaapi_filter_set_denoising_level (postproc->filter,
537             postproc->denoise_level))
538       return FALSE;
539
540     if (gst_vaapi_filter_get_denoising_level_default (postproc->filter) ==
541         postproc->denoise_level)
542       postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_DENOISE);
543   }
544
545   if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_SHARPEN) {
546     if (!gst_vaapi_filter_set_sharpening_level (postproc->filter,
547             postproc->sharpen_level))
548       return FALSE;
549
550     if (gst_vaapi_filter_get_sharpening_level_default (postproc->filter) ==
551         postproc->sharpen_level)
552       postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_SHARPEN);
553   }
554
555   if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_HUE) {
556     if (!gst_vaapi_filter_set_hue (postproc->filter, postproc->hue))
557       return FALSE;
558
559     if (gst_vaapi_filter_get_hue_default (postproc->filter) == postproc->hue)
560       postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_HUE);
561   }
562
563   if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_SATURATION) {
564     if (!gst_vaapi_filter_set_saturation (postproc->filter,
565             postproc->saturation))
566       return FALSE;
567
568     if (gst_vaapi_filter_get_saturation_default (postproc->filter) ==
569         postproc->saturation)
570       postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_SATURATION);
571   }
572
573   if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_BRIGHTNESS) {
574     if (!gst_vaapi_filter_set_brightness (postproc->filter,
575             postproc->brightness))
576       return FALSE;
577
578     if (gst_vaapi_filter_get_brightness_default (postproc->filter) ==
579         postproc->brightness)
580       postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_BRIGHTNESS);
581   }
582
583   if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_CONTRAST) {
584     if (!gst_vaapi_filter_set_contrast (postproc->filter, postproc->contrast))
585       return FALSE;
586
587     if (gst_vaapi_filter_get_contrast_default (postproc->filter) ==
588         postproc->contrast)
589       postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_CONTRAST);
590   }
591
592   if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_SCALE) {
593     if (!gst_vaapi_filter_set_scaling (postproc->filter,
594             postproc->scale_method))
595       return FALSE;
596
597     if (gst_vaapi_filter_get_scaling_default (postproc->filter) ==
598         postproc->scale_method)
599       postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_SCALE);
600   }
601
602   if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_VIDEO_DIRECTION) {
603     GstVideoOrientationMethod method = postproc->video_direction;
604     if (method == GST_VIDEO_ORIENTATION_AUTO)
605       method = postproc->tag_video_direction;
606
607     if (!gst_vaapi_filter_set_video_direction (postproc->filter, method)) {
608       GST_ELEMENT_WARNING (postproc, LIBRARY, SETTINGS,
609           ("Unsupported video direction '%s' by driver.",
610               gst_vaapi_enum_type_get_nick
611               (GST_TYPE_VIDEO_ORIENTATION_METHOD, method)),
612           ("video direction transformation ignored"));
613
614       /* Don't return FALSE because other filters might be set */
615     }
616
617     if (gst_vaapi_filter_get_video_direction_default (postproc->filter) ==
618         method)
619       postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_VIDEO_DIRECTION);
620   }
621
622   if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_SKINTONE) {
623     if (!gst_vaapi_filter_set_skintone (postproc->filter,
624             postproc->skintone_enhance))
625       return FALSE;
626
627     if (gst_vaapi_filter_get_skintone_default (postproc->filter) ==
628         postproc->skintone_enhance)
629       postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_SKINTONE);
630   }
631
632   return TRUE;
633 }
634
635 static void
636 gst_vaapipostproc_set_passthrough (GstBaseTransform * trans)
637 {
638   GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
639   gboolean filter_updated = FALSE;
640
641   if (check_filter_update (postproc) && update_filter (postproc)) {
642     /* check again if changed value is default */
643     filter_updated = check_filter_update (postproc);
644   }
645
646   gst_base_transform_set_passthrough (trans, postproc->same_caps
647       && !filter_updated);
648 }
649
650 static gboolean
651 replace_to_dumb_buffer_if_required (GstVaapiPostproc * postproc,
652     GstBuffer ** fieldbuf)
653 {
654   GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (postproc);
655   GstBuffer *newbuf;
656
657   if (!GST_VAAPI_PLUGIN_BASE_COPY_OUTPUT_FRAME (postproc))
658     return TRUE;
659
660   newbuf = create_output_dump_buffer (postproc);
661   if (!newbuf)
662     return FALSE;
663
664   if (!gst_vaapi_plugin_copy_va_buffer (plugin, *fieldbuf, newbuf)) {
665     gst_buffer_unref (newbuf);
666     return FALSE;
667   }
668
669   gst_buffer_replace (fieldbuf, newbuf);
670   gst_buffer_unref (newbuf);
671
672   return TRUE;
673 }
674
675 static gboolean
676 use_vpp_crop (GstVaapiPostproc * postproc)
677 {
678   return !postproc->forward_crop;
679 }
680
681 static GstFlowReturn
682 gst_vaapipostproc_process_vpp (GstBaseTransform * trans, GstBuffer * inbuf,
683     GstBuffer * outbuf)
684 {
685   GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
686   GstVaapiDeinterlaceState *const ds = &postproc->deinterlace_state;
687   GstVaapiVideoMeta *inbuf_meta, *outbuf_meta;
688   GstVaapiSurface *inbuf_surface, *outbuf_surface;
689   GstVaapiSurfaceProxy *proxy;
690   GstVaapiFilterStatus status;
691   GstClockTime timestamp;
692   GstFlowReturn ret;
693   GstBuffer *fieldbuf;
694   GstVaapiDeinterlaceMethod deint_method;
695   guint flags, deint_flags;
696   gboolean tff, deint, deint_refs, deint_changed, discont;
697   const GstVideoCropMeta *crop_meta;
698   GstVaapiRectangle *crop_rect = NULL;
699   GstVaapiRectangle tmp_rect;
700
701   inbuf_meta = gst_buffer_get_vaapi_video_meta (inbuf);
702   if (!inbuf_meta)
703     goto error_invalid_buffer;
704   inbuf_surface = gst_vaapi_video_meta_get_surface (inbuf_meta);
705
706   crop_meta = gst_buffer_get_video_crop_meta (inbuf);
707   if (crop_meta && use_vpp_crop (postproc)) {
708     GST_DEBUG_OBJECT (postproc, "cropping x=%d,y=%d,w=%d,h=%d",
709         crop_meta->x, crop_meta->y, crop_meta->width, crop_meta->height);
710     crop_rect = &tmp_rect;
711     crop_rect->x = crop_meta->x;
712     crop_rect->y = crop_meta->y;
713     crop_rect->width = crop_meta->width;
714     crop_rect->height = crop_meta->height;
715   }
716   if (!crop_rect)
717     crop_rect = (GstVaapiRectangle *)
718         gst_vaapi_video_meta_get_render_rect (inbuf_meta);
719
720   timestamp = GST_BUFFER_TIMESTAMP (inbuf);
721   tff = GST_BUFFER_FLAG_IS_SET (inbuf, GST_VIDEO_BUFFER_FLAG_TFF);
722   discont = GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_DISCONT);
723   deint = should_deinterlace_buffer (postproc, inbuf);
724
725   /* Drop references if deinterlacing conditions changed */
726   deint_changed = deint != ds->deint;
727   if (deint_changed || (ds->num_surfaces > 0 && tff != ds->tff))
728     ds_reset (ds);
729
730   deint_method = postproc->deinterlace_method;
731   deint_refs = deint_method_is_advanced (deint_method);
732   if (deint_refs && 0) {
733     GstBuffer *const prev_buf = ds_get_buffer (ds, 0);
734     GstClockTime prev_pts, pts = GST_BUFFER_TIMESTAMP (inbuf);
735     /* Reset deinterlacing state when there is a discontinuity */
736     if (prev_buf && (prev_pts = GST_BUFFER_TIMESTAMP (prev_buf)) != pts) {
737       const GstClockTimeDiff pts_diff = GST_CLOCK_DIFF (prev_pts, pts);
738       if (pts_diff < 0 || (postproc->field_duration > 0 &&
739               pts_diff >= postproc->field_duration * 3 - 1))
740         ds_reset (ds);
741     }
742   }
743
744   ds->deint = deint;
745   ds->tff = tff;
746
747   flags = gst_vaapi_video_meta_get_render_flags (inbuf_meta) &
748       ~GST_VAAPI_PICTURE_STRUCTURE_MASK;
749
750   /* First field */
751   if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) {
752     fieldbuf = create_output_buffer (postproc);
753     if (!fieldbuf)
754       goto error_create_buffer;
755
756     outbuf_meta = gst_buffer_get_vaapi_video_meta (fieldbuf);
757     if (!outbuf_meta)
758       goto error_create_meta;
759
760     if (!gst_vaapi_video_meta_get_surface_proxy (outbuf_meta)) {
761       proxy =
762           gst_vaapi_surface_proxy_new_from_pool (GST_VAAPI_SURFACE_POOL
763           (postproc->filter_pool));
764       if (!proxy)
765         goto error_create_proxy;
766       gst_vaapi_video_meta_set_surface_proxy (outbuf_meta, proxy);
767       gst_vaapi_surface_proxy_unref (proxy);
768     }
769
770     if (deint) {
771       deint_flags = (tff ? GST_VAAPI_DEINTERLACE_FLAG_TOPFIELD : 0);
772       if (tff)
773         deint_flags |= GST_VAAPI_DEINTERLACE_FLAG_TFF;
774       if (!set_best_deint_method (postproc, deint_flags, &deint_method))
775         goto error_op_deinterlace;
776
777       if (deint_method != postproc->deinterlace_method) {
778         GST_DEBUG ("unsupported deinterlace-method %u. Using %u instead",
779             postproc->deinterlace_method, deint_method);
780         postproc->deinterlace_method = deint_method;
781         deint_refs = deint_method_is_advanced (deint_method);
782       }
783
784       if (deint_refs) {
785         ds_set_surfaces (ds);
786         if (!gst_vaapi_filter_set_deinterlacing_references (postproc->filter,
787                 ds->surfaces, ds->num_surfaces, NULL, 0))
788           goto error_op_deinterlace;
789       }
790     } else if (deint_changed) {
791       // Reset internal filter to non-deinterlacing mode
792       deint_method = GST_VAAPI_DEINTERLACE_METHOD_NONE;
793       if (!gst_vaapi_filter_set_deinterlacing (postproc->filter,
794               deint_method, 0))
795         goto error_op_deinterlace;
796     }
797
798     outbuf_surface = gst_vaapi_video_meta_get_surface (outbuf_meta);
799     gst_vaapi_filter_set_cropping_rectangle (postproc->filter, crop_rect);
800     status = gst_vaapi_filter_process (postproc->filter, inbuf_surface,
801         outbuf_surface, flags);
802     if (status != GST_VAAPI_FILTER_STATUS_SUCCESS)
803       goto error_process_vpp;
804
805     copy_metadata (postproc, fieldbuf, inbuf);
806     GST_BUFFER_TIMESTAMP (fieldbuf) = timestamp;
807     GST_BUFFER_DURATION (fieldbuf) = postproc->field_duration;
808     if (discont) {
809       GST_BUFFER_FLAG_SET (fieldbuf, GST_BUFFER_FLAG_DISCONT);
810       discont = FALSE;
811     }
812
813     if (!replace_to_dumb_buffer_if_required (postproc, &fieldbuf))
814       goto error_copy_buffer;
815
816     ret = gst_pad_push (trans->srcpad, fieldbuf);
817     if (ret != GST_FLOW_OK)
818       goto error_push_buffer;
819   }
820   fieldbuf = NULL;
821
822   /* Second field */
823   outbuf_meta = gst_buffer_get_vaapi_video_meta (outbuf);
824   if (!outbuf_meta)
825     goto error_create_meta;
826
827   if (!gst_vaapi_video_meta_get_surface_proxy (outbuf_meta)) {
828     proxy =
829         gst_vaapi_surface_proxy_new_from_pool (GST_VAAPI_SURFACE_POOL
830         (postproc->filter_pool));
831     if (!proxy)
832       goto error_create_proxy;
833     gst_vaapi_video_meta_set_surface_proxy (outbuf_meta, proxy);
834     gst_vaapi_surface_proxy_unref (proxy);
835   }
836
837   if (deint) {
838     deint_flags = (tff ? 0 : GST_VAAPI_DEINTERLACE_FLAG_TOPFIELD);
839     if (tff)
840       deint_flags |= GST_VAAPI_DEINTERLACE_FLAG_TFF;
841     if (!gst_vaapi_filter_set_deinterlacing (postproc->filter,
842             deint_method, deint_flags))
843       goto error_op_deinterlace;
844
845     if (deint_refs
846         && !gst_vaapi_filter_set_deinterlacing_references (postproc->filter,
847             ds->surfaces, ds->num_surfaces, NULL, 0))
848       goto error_op_deinterlace;
849   } else if (deint_changed
850       && !gst_vaapi_filter_set_deinterlacing (postproc->filter, deint_method,
851           0))
852     goto error_op_deinterlace;
853
854   outbuf_surface = gst_vaapi_video_meta_get_surface (outbuf_meta);
855   gst_vaapi_filter_set_cropping_rectangle (postproc->filter, crop_rect);
856   status = gst_vaapi_filter_process (postproc->filter, inbuf_surface,
857       outbuf_surface, flags);
858   if (status != GST_VAAPI_FILTER_STATUS_SUCCESS)
859     goto error_process_vpp;
860
861   if (!(postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE))
862     gst_buffer_copy_into (outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
863   else {
864     GST_BUFFER_TIMESTAMP (outbuf) = timestamp + postproc->field_duration;
865     GST_BUFFER_DURATION (outbuf) = postproc->field_duration;
866     if (discont) {
867       GST_BUFFER_FLAG_SET (fieldbuf, GST_BUFFER_FLAG_DISCONT);
868       discont = FALSE;
869     }
870   }
871
872   copy_metadata (postproc, outbuf, inbuf);
873
874   if (deint && deint_refs)
875     ds_add_buffer (ds, inbuf);
876   postproc->use_vpp = TRUE;
877   return GST_FLOW_OK;
878
879   /* ERRORS */
880 error_invalid_buffer:
881   {
882     GST_ERROR_OBJECT (postproc, "failed to validate source buffer");
883     return GST_FLOW_ERROR;
884   }
885 error_create_buffer:
886   {
887     GST_ERROR_OBJECT (postproc, "failed to create output buffer");
888     return GST_FLOW_ERROR;
889   }
890 error_create_meta:
891   {
892     GST_ERROR_OBJECT (postproc, "failed to create new output buffer meta");
893     gst_buffer_replace (&fieldbuf, NULL);
894     return GST_FLOW_ERROR;
895   }
896 error_create_proxy:
897   {
898     GST_ERROR_OBJECT (postproc, "failed to create surface proxy from pool");
899     gst_buffer_replace (&fieldbuf, NULL);
900     return GST_FLOW_ERROR;
901   }
902 error_op_deinterlace:
903   {
904     GST_ERROR_OBJECT (postproc, "failed to apply deinterlacing filter");
905     gst_buffer_replace (&fieldbuf, NULL);
906     return GST_FLOW_NOT_SUPPORTED;
907   }
908 error_process_vpp:
909   {
910     GST_ERROR_OBJECT (postproc, "failed to apply VPP filters (error %d)",
911         status);
912     gst_buffer_replace (&fieldbuf, NULL);
913     return GST_FLOW_ERROR;
914   }
915 error_copy_buffer:
916   {
917     GST_ERROR_OBJECT (postproc, "failed to copy field buffer to dumb buffer");
918     gst_buffer_replace (&fieldbuf, NULL);
919     return GST_FLOW_ERROR;
920   }
921 error_push_buffer:
922   {
923     GST_DEBUG_OBJECT (postproc, "failed to push output buffer: %s",
924         gst_flow_get_name (ret));
925     return ret;
926   }
927 }
928
929 static GstFlowReturn
930 gst_vaapipostproc_process (GstBaseTransform * trans, GstBuffer * inbuf,
931     GstBuffer * outbuf)
932 {
933   GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
934   GstVaapiVideoMeta *meta;
935   GstClockTime timestamp;
936   GstFlowReturn ret;
937   GstBuffer *fieldbuf;
938   guint fieldbuf_flags, outbuf_flags, flags;
939   gboolean tff, deint;
940
941   meta = gst_buffer_get_vaapi_video_meta (inbuf);
942   if (!meta)
943     goto error_invalid_buffer;
944
945   timestamp = GST_BUFFER_TIMESTAMP (inbuf);
946   tff = GST_BUFFER_FLAG_IS_SET (inbuf, GST_VIDEO_BUFFER_FLAG_TFF);
947   deint = should_deinterlace_buffer (postproc, inbuf);
948
949   flags = gst_vaapi_video_meta_get_render_flags (meta) &
950       ~GST_VAAPI_PICTURE_STRUCTURE_MASK;
951
952   /* First field */
953   fieldbuf = create_output_buffer (postproc);
954   if (!fieldbuf)
955     goto error_create_buffer;
956   append_output_buffer_metadata (postproc, fieldbuf, inbuf, 0);
957
958   meta = gst_buffer_get_vaapi_video_meta (fieldbuf);
959   fieldbuf_flags = flags;
960   fieldbuf_flags |= deint ? (tff ?
961       GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD :
962       GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD) :
963       GST_VAAPI_PICTURE_STRUCTURE_FRAME;
964   gst_vaapi_video_meta_set_render_flags (meta, fieldbuf_flags);
965
966   GST_BUFFER_TIMESTAMP (fieldbuf) = timestamp;
967   GST_BUFFER_DURATION (fieldbuf) = postproc->field_duration;
968
969   if (!replace_to_dumb_buffer_if_required (postproc, &fieldbuf))
970     goto error_copy_buffer;
971
972   ret = gst_pad_push (trans->srcpad, fieldbuf);
973   if (ret != GST_FLOW_OK)
974     goto error_push_buffer;
975
976   /* Second field */
977   append_output_buffer_metadata (postproc, outbuf, inbuf, 0);
978
979   meta = gst_buffer_get_vaapi_video_meta (outbuf);
980   outbuf_flags = flags;
981   outbuf_flags |= deint ? (tff ?
982       GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD :
983       GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD) :
984       GST_VAAPI_PICTURE_STRUCTURE_FRAME;
985   gst_vaapi_video_meta_set_render_flags (meta, outbuf_flags);
986
987   GST_BUFFER_TIMESTAMP (outbuf) = timestamp + postproc->field_duration;
988   GST_BUFFER_DURATION (outbuf) = postproc->field_duration;
989   return GST_FLOW_OK;
990
991   /* ERRORS */
992 error_invalid_buffer:
993   {
994     GST_ERROR_OBJECT (postproc, "failed to validate source buffer");
995     return GST_FLOW_ERROR;
996   }
997 error_create_buffer:
998   {
999     GST_ERROR_OBJECT (postproc, "failed to create output buffer");
1000     return GST_FLOW_EOS;
1001   }
1002 error_copy_buffer:
1003   {
1004     GST_ERROR_OBJECT (postproc, "failed to copy field buffer to dumb buffer");
1005     gst_buffer_replace (&fieldbuf, NULL);
1006     return GST_FLOW_ERROR;
1007   }
1008 error_push_buffer:
1009   {
1010     GST_DEBUG_OBJECT (postproc, "failed to push output buffer: %s",
1011         gst_flow_get_name (ret));
1012     return ret;
1013   }
1014 }
1015
1016 static GstFlowReturn
1017 gst_vaapipostproc_passthrough (GstBaseTransform * trans, GstBuffer * inbuf,
1018     GstBuffer * outbuf)
1019 {
1020   GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1021   GstVaapiVideoMeta *meta;
1022
1023   /* No video processing needed, simply copy buffer metadata */
1024   meta = gst_buffer_get_vaapi_video_meta (inbuf);
1025   if (!meta)
1026     goto error_invalid_buffer;
1027
1028   append_output_buffer_metadata (postproc, outbuf, inbuf,
1029       GST_BUFFER_COPY_TIMESTAMPS);
1030   return GST_FLOW_OK;
1031
1032   /* ERRORS */
1033 error_invalid_buffer:
1034   {
1035     GST_ERROR_OBJECT (postproc, "failed to validate source buffer");
1036     return GST_FLOW_ERROR;
1037   }
1038 }
1039
1040 static gboolean
1041 video_info_changed (GstVideoInfo * old_vip, GstVideoInfo * new_vip)
1042 {
1043   if (gst_video_info_changed (old_vip, new_vip))
1044     return TRUE;
1045   if (GST_VIDEO_INFO_INTERLACE_MODE (old_vip) !=
1046       GST_VIDEO_INFO_INTERLACE_MODE (new_vip))
1047     return TRUE;
1048   return FALSE;
1049 }
1050
1051 static gboolean
1052 video_info_update (GstCaps * caps, GstVideoInfo * info,
1053     gboolean * caps_changed_ptr)
1054 {
1055   GstVideoInfo vi;
1056
1057   if (!gst_video_info_from_caps (&vi, caps))
1058     return FALSE;
1059
1060   *caps_changed_ptr = FALSE;
1061   if (video_info_changed (info, &vi)) {
1062     *caps_changed_ptr = TRUE;
1063     *info = vi;
1064   }
1065
1066   return TRUE;
1067 }
1068
1069 static gboolean
1070 gst_vaapipostproc_update_sink_caps (GstVaapiPostproc * postproc, GstCaps * caps,
1071     gboolean * caps_changed_ptr)
1072 {
1073   GstVideoInfo vi;
1074   gboolean deinterlace;
1075
1076   GST_INFO_OBJECT (postproc, "new sink caps = %" GST_PTR_FORMAT, caps);
1077
1078   if (!video_info_update (caps, &postproc->sinkpad_info, caps_changed_ptr))
1079     return FALSE;
1080
1081   vi = postproc->sinkpad_info;
1082   deinterlace = is_deinterlace_enabled (postproc, &vi);
1083   if (deinterlace)
1084     postproc->flags |= GST_VAAPI_POSTPROC_FLAG_DEINTERLACE;
1085   postproc->field_duration = GST_VIDEO_INFO_FPS_N (&vi) > 0 ?
1086       gst_util_uint64_scale (GST_SECOND, GST_VIDEO_INFO_FPS_D (&vi),
1087       (1 + deinterlace) * GST_VIDEO_INFO_FPS_N (&vi)) : 0;
1088
1089   postproc->get_va_surfaces = gst_caps_has_vaapi_surface (caps);
1090   return TRUE;
1091 }
1092
1093 static gboolean
1094 gst_vaapipostproc_update_src_caps (GstVaapiPostproc * postproc, GstCaps * caps,
1095     gboolean * caps_changed_ptr)
1096 {
1097   GST_INFO_OBJECT (postproc, "new src caps = %" GST_PTR_FORMAT, caps);
1098
1099   if (!video_info_update (caps, &postproc->srcpad_info, caps_changed_ptr))
1100     return FALSE;
1101
1102   if (postproc->format != GST_VIDEO_INFO_FORMAT (&postproc->sinkpad_info) &&
1103       postproc->format != DEFAULT_FORMAT)
1104     postproc->flags |= GST_VAAPI_POSTPROC_FLAG_FORMAT;
1105
1106   if (GST_VIDEO_INFO_WIDTH (&postproc->srcpad_info) !=
1107       GST_VIDEO_INFO_WIDTH (&postproc->sinkpad_info)
1108       || GST_VIDEO_INFO_HEIGHT (&postproc->srcpad_info) !=
1109       GST_VIDEO_INFO_HEIGHT (&postproc->sinkpad_info))
1110     postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SIZE;
1111
1112   return TRUE;
1113 }
1114
1115 static gboolean
1116 ensure_allowed_sinkpad_caps (GstVaapiPostproc * postproc)
1117 {
1118   GstCaps *out_caps, *raw_caps;
1119   guint i, num_structures;
1120
1121   if (postproc->allowed_sinkpad_caps)
1122     return TRUE;
1123
1124   if (!GST_VAAPI_PLUGIN_BASE_DISPLAY (postproc))
1125     return FALSE;
1126
1127   /* Create VA caps */
1128   out_caps = gst_caps_from_string (GST_VAAPI_MAKE_SURFACE_CAPS ", "
1129       GST_CAPS_INTERLACED_MODES);
1130   if (!out_caps) {
1131     GST_WARNING_OBJECT (postproc, "failed to create VA sink caps");
1132     return FALSE;
1133   }
1134
1135   raw_caps = gst_vaapi_plugin_base_get_allowed_sinkpad_raw_caps
1136       (GST_VAAPI_PLUGIN_BASE (postproc));
1137   if (!raw_caps) {
1138     gst_caps_unref (out_caps);
1139     GST_WARNING_OBJECT (postproc, "failed to create YUV sink caps");
1140     return FALSE;
1141   }
1142
1143   out_caps = gst_caps_make_writable (out_caps);
1144   gst_caps_append (out_caps, gst_caps_copy (raw_caps));
1145
1146   num_structures = gst_caps_get_size (out_caps);
1147   for (i = 0; i < num_structures; i++) {
1148     GstStructure *structure;
1149
1150     structure = gst_caps_get_structure (out_caps, i);
1151     if (!structure)
1152       continue;
1153
1154     if (postproc->filter)
1155       gst_vaapi_filter_append_caps (postproc->filter, structure);
1156   }
1157
1158   postproc->allowed_sinkpad_caps = out_caps;
1159
1160   /* XXX: append VA/VPP filters */
1161   return TRUE;
1162 }
1163
1164 /* Fixup output caps so that to reflect the supported set of pixel formats */
1165 static GstCaps *
1166 expand_allowed_srcpad_caps (GstVaapiPostproc * postproc, GstCaps * caps)
1167 {
1168   GValue value = G_VALUE_INIT, v_format = G_VALUE_INIT;
1169   guint i, num_structures;
1170   gint gl_upload_meta_idx = -1;
1171
1172   if (postproc->filter == NULL)
1173     goto cleanup;
1174   if (!gst_vaapipostproc_ensure_filter_caps (postproc))
1175     goto cleanup;
1176
1177   /* Reset "format" field for each structure */
1178   if (!gst_vaapi_value_set_format_list (&value, postproc->filter_formats))
1179     goto cleanup;
1180   if (gst_vaapi_value_set_format (&v_format, GST_VIDEO_FORMAT_ENCODED)) {
1181     gst_value_list_prepend_value (&value, &v_format);
1182     g_value_unset (&v_format);
1183   }
1184
1185   num_structures = gst_caps_get_size (caps);
1186   for (i = 0; i < num_structures; i++) {
1187     GstCapsFeatures *const features = gst_caps_get_features (caps, i);
1188     GstStructure *structure;
1189
1190     structure = gst_caps_get_structure (caps, i);
1191     if (!structure)
1192       continue;
1193
1194     gst_vaapi_filter_append_caps (postproc->filter, structure);
1195
1196     if (gst_caps_features_contains (features,
1197             GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META)) {
1198       gl_upload_meta_idx = i;
1199       continue;
1200     }
1201
1202     gst_structure_set_value (structure, "format", &value);
1203   }
1204   g_value_unset (&value);
1205
1206   if ((GST_VAAPI_PLUGIN_BASE_SRC_PAD_CAN_DMABUF (postproc)
1207           || !gst_vaapi_display_has_opengl (GST_VAAPI_PLUGIN_BASE_DISPLAY
1208               (postproc)))
1209       && gl_upload_meta_idx > -1) {
1210     gst_caps_remove_structure (caps, gl_upload_meta_idx);
1211   }
1212
1213 cleanup:
1214   return caps;
1215 }
1216
1217 static gboolean
1218 ensure_allowed_srcpad_caps (GstVaapiPostproc * postproc)
1219 {
1220   GstCaps *out_caps;
1221
1222   if (postproc->allowed_srcpad_caps)
1223     return TRUE;
1224
1225   /* Create initial caps from pad template */
1226   out_caps = gst_caps_from_string (gst_vaapipostproc_src_caps_str);
1227   if (!out_caps) {
1228     GST_ERROR_OBJECT (postproc, "failed to create VA src caps");
1229     return FALSE;
1230   }
1231
1232   postproc->allowed_srcpad_caps =
1233       expand_allowed_srcpad_caps (postproc, out_caps);
1234   return postproc->allowed_srcpad_caps != NULL;
1235 }
1236
1237 static GstCaps *
1238 gst_vaapipostproc_transform_caps_impl (GstBaseTransform * trans,
1239     GstPadDirection direction)
1240 {
1241   GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1242
1243   /* Generate the sink pad caps, that could be fixated afterwards */
1244   if (direction == GST_PAD_SRC) {
1245     if (!ensure_allowed_sinkpad_caps (postproc))
1246       return gst_caps_from_string (gst_vaapipostproc_sink_caps_str);
1247     return gst_caps_ref (postproc->allowed_sinkpad_caps);
1248   }
1249
1250   /* Generate complete set of src pad caps */
1251   if (!ensure_allowed_srcpad_caps (postproc))
1252     return NULL;
1253   return gst_vaapipostproc_transform_srccaps (postproc);
1254 }
1255
1256 static GstCaps *
1257 gst_vaapipostproc_transform_caps (GstBaseTransform * trans,
1258     GstPadDirection direction, GstCaps * caps, GstCaps * filter)
1259 {
1260   GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1261   GstCaps *out_caps;
1262
1263   GST_DEBUG_OBJECT (trans,
1264       "Transforming caps %" GST_PTR_FORMAT " in direction %s", caps,
1265       (direction == GST_PAD_SINK) ? "sink" : "src");
1266
1267   g_mutex_lock (&postproc->postproc_lock);
1268   out_caps = gst_vaapipostproc_transform_caps_impl (trans, direction);
1269   g_mutex_unlock (&postproc->postproc_lock);
1270
1271   if (out_caps && filter) {
1272     GstCaps *intersection;
1273
1274     intersection = gst_caps_intersect_full (out_caps, filter,
1275         GST_CAPS_INTERSECT_FIRST);
1276     gst_caps_unref (out_caps);
1277     out_caps = intersection;
1278   }
1279
1280   GST_DEBUG_OBJECT (trans, "returning caps: %" GST_PTR_FORMAT, out_caps);
1281
1282   return out_caps;
1283 }
1284
1285 static GstCaps *
1286 gst_vaapipostproc_fixate_caps (GstBaseTransform * trans,
1287     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
1288 {
1289   GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1290   GstCaps *outcaps = NULL;
1291   gboolean same_caps, filter_updated = FALSE;
1292
1293   GST_DEBUG_OBJECT (trans, "trying to fixate othercaps %" GST_PTR_FORMAT
1294       " based on caps %" GST_PTR_FORMAT " in direction %s", othercaps, caps,
1295       (direction == GST_PAD_SINK) ? "sink" : "src");
1296
1297   if (direction == GST_PAD_SRC) {
1298     /* @TODO: we can do better */
1299     outcaps = gst_caps_fixate (othercaps);
1300     goto done;
1301   }
1302
1303   g_mutex_lock (&postproc->postproc_lock);
1304   postproc->has_vpp = gst_vaapipostproc_ensure_filter_caps (postproc);
1305   if (check_filter_update (postproc) && update_filter (postproc)) {
1306     /* check again if changed value is default */
1307     filter_updated = check_filter_update (postproc);
1308   }
1309
1310   outcaps = gst_vaapipostproc_fixate_srccaps (postproc, caps, othercaps);
1311   g_mutex_unlock (&postproc->postproc_lock);
1312
1313   /* set passthrough according to caps changes or filter changes */
1314   same_caps = gst_caps_is_equal (caps, outcaps);
1315   gst_base_transform_set_passthrough (trans, same_caps && !filter_updated);
1316
1317 done:
1318   GST_DEBUG_OBJECT (trans, "fixated othercaps to %" GST_PTR_FORMAT, outcaps);
1319   gst_caps_unref (othercaps);
1320
1321   return outcaps;
1322 }
1323
1324 static gboolean
1325 gst_vaapipostproc_transform_size (GstBaseTransform * trans,
1326     GstPadDirection direction, GstCaps * caps, gsize size,
1327     GstCaps * othercaps, gsize * othersize)
1328 {
1329   GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1330
1331   if (direction == GST_PAD_SINK || postproc->get_va_surfaces)
1332     *othersize = 0;
1333   else
1334     *othersize = size;
1335   return TRUE;
1336 }
1337
1338 static gboolean
1339 gst_vaapipostproc_transform_meta (GstBaseTransform * trans, GstBuffer * outbuf,
1340     GstMeta * meta, GstBuffer * inbuf)
1341 {
1342   GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1343
1344   /* don't copy GstVideoCropMeta if we are using vpp crop */
1345   if (meta->info->api == GST_VIDEO_CROP_META_API_TYPE
1346       && use_vpp_crop (postproc))
1347     return FALSE;
1348
1349   /* don't copy GstParentBufferMeta if use_vpp */
1350   if (meta->info->api == GST_PARENT_BUFFER_META_API_TYPE && postproc->use_vpp)
1351     return FALSE;
1352
1353   return TRUE;
1354 }
1355
1356 static GstFlowReturn
1357 gst_vaapipostproc_transform (GstBaseTransform * trans, GstBuffer * inbuf,
1358     GstBuffer * outbuf)
1359 {
1360   GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1361   GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (postproc);
1362   GstBuffer *buf, *sys_buf = NULL;
1363   GstFlowReturn ret;
1364
1365   ret = gst_vaapi_plugin_base_get_input_buffer (plugin, inbuf, &buf);
1366   if (ret != GST_FLOW_OK)
1367     return GST_FLOW_ERROR;
1368
1369   if (GST_VAAPI_PLUGIN_BASE_COPY_OUTPUT_FRAME (trans)) {
1370     GstBuffer *va_buf = create_output_buffer (postproc);
1371     if (!va_buf) {
1372       ret = GST_FLOW_ERROR;
1373       goto done;
1374     }
1375     sys_buf = outbuf;
1376     outbuf = va_buf;
1377   }
1378
1379   ret = GST_FLOW_NOT_SUPPORTED;
1380   if (postproc->flags) {
1381     /* Use VA/VPP extensions to process this frame */
1382     if (postproc->has_vpp) {
1383       ret = gst_vaapipostproc_process_vpp (trans, buf, outbuf);
1384       if (ret != GST_FLOW_NOT_SUPPORTED)
1385         goto done;
1386       GST_WARNING_OBJECT (postproc, "unsupported VPP filters. Disabling");
1387     }
1388
1389     /* Only append picture structure meta data (top/bottom field) */
1390     if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) {
1391       ret = gst_vaapipostproc_process (trans, buf, outbuf);
1392       if (ret != GST_FLOW_NOT_SUPPORTED)
1393         goto done;
1394     }
1395   }
1396
1397   /* Fallback: passthrough to the downstream element as is */
1398   ret = gst_vaapipostproc_passthrough (trans, buf, outbuf);
1399
1400 done:
1401   gst_buffer_unref (buf);
1402
1403   if (sys_buf) {
1404     if (!gst_vaapi_plugin_copy_va_buffer (plugin, outbuf, sys_buf))
1405       return GST_FLOW_ERROR;
1406
1407     gst_buffer_unref (outbuf);
1408     outbuf = sys_buf;
1409   }
1410
1411   return ret;
1412 }
1413
1414 static gboolean
1415 ensure_buffer_pool (GstVaapiPostproc * postproc, GstVideoInfo * vi)
1416 {
1417   GstVaapiVideoPool *pool;
1418
1419   if (!vi)
1420     return FALSE;
1421
1422   gst_video_info_change_format (vi, postproc->format,
1423       GST_VIDEO_INFO_WIDTH (vi), GST_VIDEO_INFO_HEIGHT (vi));
1424
1425   if (postproc->filter_pool
1426       && !video_info_changed (&postproc->filter_pool_info, vi))
1427     return TRUE;
1428   postproc->filter_pool_info = *vi;
1429
1430   pool =
1431       gst_vaapi_surface_pool_new_full (GST_VAAPI_PLUGIN_BASE_DISPLAY (postproc),
1432       &postproc->filter_pool_info, 0);
1433   if (!pool)
1434     return FALSE;
1435
1436   gst_vaapi_video_pool_replace (&postproc->filter_pool, pool);
1437   gst_vaapi_video_pool_unref (pool);
1438   return TRUE;
1439 }
1440
1441 static GstFlowReturn
1442 gst_vaapipostproc_prepare_output_buffer (GstBaseTransform * trans,
1443     GstBuffer * inbuf, GstBuffer ** outbuf_ptr)
1444 {
1445   GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1446   const GstVideoCropMeta *crop_meta;
1447   GstVideoInfo info;
1448
1449   if (gst_base_transform_is_passthrough (trans)) {
1450     *outbuf_ptr = inbuf;
1451     return GST_FLOW_OK;
1452   }
1453
1454   /* If we are not using vpp crop (i.e. forwarding crop meta to downstream)
1455    * then, ensure our output buffer pool is sized for uncropped output */
1456   crop_meta = gst_buffer_get_video_crop_meta (inbuf);
1457   if (crop_meta && !use_vpp_crop (postproc)) {
1458     info = postproc->srcpad_info;
1459     info.width += crop_meta->x;
1460     info.height += crop_meta->y;
1461     ensure_buffer_pool (postproc, &info);
1462   }
1463
1464   if (GST_VAAPI_PLUGIN_BASE_COPY_OUTPUT_FRAME (trans)) {
1465     *outbuf_ptr = create_output_dump_buffer (postproc);
1466   } else {
1467     *outbuf_ptr = create_output_buffer (postproc);
1468   }
1469
1470   if (!*outbuf_ptr)
1471     return GST_FLOW_ERROR;
1472
1473   return GST_FLOW_OK;
1474 }
1475
1476 static gboolean
1477 ensure_srcpad_buffer_pool (GstVaapiPostproc * postproc, GstCaps * caps)
1478 {
1479   GstVideoInfo vi;
1480
1481   if (!gst_video_info_from_caps (&vi, caps))
1482     return FALSE;
1483
1484   return ensure_buffer_pool (postproc, &vi);
1485 }
1486
1487 static gboolean
1488 is_native_video_format (GstVideoFormat format)
1489 {
1490   guint i = 0;
1491   for (i = 0; i < G_N_ELEMENTS (native_formats); i++)
1492     if (native_formats[i] == format)
1493       return TRUE;
1494   return FALSE;
1495 }
1496
1497 static gboolean
1498 gst_vaapipostproc_set_caps (GstBaseTransform * trans, GstCaps * caps,
1499     GstCaps * out_caps)
1500 {
1501   GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1502   gboolean sink_caps_changed = FALSE;
1503   gboolean src_caps_changed = FALSE;
1504   GstVideoInfo vinfo;
1505   gboolean ret = FALSE;
1506
1507   g_mutex_lock (&postproc->postproc_lock);
1508   if (!gst_vaapipostproc_update_sink_caps (postproc, caps, &sink_caps_changed))
1509     goto done;
1510   /* HACK: This is a workaround to deal with the va-intel-driver for non-native
1511    * formats while doing advanced deinterlacing. The format of reference surfaces must
1512    * be same as the format used by the driver internally for motion adaptive
1513    * deinterlacing and motion compensated deinterlacing */
1514   if (!gst_video_info_from_caps (&vinfo, caps))
1515     goto done;
1516   if (deint_method_is_advanced (postproc->deinterlace_method)
1517       && !is_native_video_format (GST_VIDEO_INFO_FORMAT (&vinfo))) {
1518     GST_WARNING_OBJECT (postproc,
1519         "Advanced deinterlacing requires the native video formats used by the driver internally");
1520     goto done;
1521   }
1522   if (!gst_vaapipostproc_update_src_caps (postproc, out_caps,
1523           &src_caps_changed))
1524     goto done;
1525
1526   if (sink_caps_changed || src_caps_changed) {
1527     gst_vaapipostproc_destroy (postproc);
1528     if (!gst_vaapipostproc_create (postproc))
1529       goto done;
1530     if (!gst_vaapi_plugin_base_set_caps (GST_VAAPI_PLUGIN_BASE (trans),
1531             caps, out_caps))
1532       goto done;
1533   }
1534
1535   if (!ensure_srcpad_buffer_pool (postproc, out_caps))
1536     goto done;
1537
1538   postproc->same_caps = gst_caps_is_equal (caps, out_caps);
1539
1540   if (!src_caps_changed) {
1541     /* set passthrough according to caps changes or filter changes */
1542     gst_vaapipostproc_set_passthrough (trans);
1543   }
1544
1545   ret = TRUE;
1546
1547 done:
1548   g_mutex_unlock (&postproc->postproc_lock);
1549
1550   /* Updates the srcpad caps and send the caps downstream */
1551   if (ret && src_caps_changed)
1552     gst_base_transform_update_src_caps (trans, out_caps);
1553
1554   return ret;
1555 }
1556
1557 static gboolean
1558 gst_vaapipostproc_query (GstBaseTransform * trans,
1559     GstPadDirection direction, GstQuery * query)
1560 {
1561   GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1562   GstElement *const element = GST_ELEMENT (trans);
1563
1564   if (GST_QUERY_TYPE (query) == GST_QUERY_CONTEXT) {
1565     if (gst_vaapi_handle_context_query (element, query)) {
1566       GST_DEBUG_OBJECT (postproc, "sharing display %" GST_PTR_FORMAT,
1567           GST_VAAPI_PLUGIN_BASE_DISPLAY (postproc));
1568       return TRUE;
1569     }
1570   }
1571
1572   return
1573       GST_BASE_TRANSFORM_CLASS (gst_vaapipostproc_parent_class)->query (trans,
1574       direction, query);
1575 }
1576
1577 static gboolean
1578 gst_vaapipostproc_propose_allocation (GstBaseTransform * trans,
1579     GstQuery * decide_query, GstQuery * query)
1580 {
1581   GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1582   GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (trans);
1583   GstCaps *allocation_caps;
1584   GstStructure *structure;
1585   gint allocation_width, allocation_height;
1586   gint negotiated_width, negotiated_height;
1587
1588   /* advertise to upstream that we can handle crop meta */
1589   if (decide_query)
1590     gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
1591
1592   negotiated_width = GST_VIDEO_INFO_WIDTH (&postproc->sinkpad_info);
1593   negotiated_height = GST_VIDEO_INFO_HEIGHT (&postproc->sinkpad_info);
1594
1595   if (negotiated_width == 0 || negotiated_height == 0)
1596     goto bail;
1597
1598   allocation_caps = NULL;
1599   gst_query_parse_allocation (query, &allocation_caps, NULL);
1600   if (!allocation_caps)
1601     goto bail;
1602
1603   structure = gst_caps_get_structure (allocation_caps, 0);
1604   if (!gst_structure_get_int (structure, "width", &allocation_width))
1605     goto bail;
1606   if (!gst_structure_get_int (structure, "height", &allocation_height))
1607     goto bail;
1608
1609   if (allocation_width != negotiated_width
1610       || allocation_height != negotiated_height) {
1611     g_mutex_lock (&postproc->postproc_lock);
1612     postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SIZE;
1613     g_mutex_unlock (&postproc->postproc_lock);
1614   }
1615
1616 bail:
1617   /* Let vaapidecode allocate the video buffers */
1618   if (postproc->get_va_surfaces)
1619     return FALSE;
1620   if (!gst_vaapi_plugin_base_propose_allocation (plugin, query))
1621     return FALSE;
1622   return TRUE;
1623 }
1624
1625 static gboolean
1626 gst_vaapipostproc_decide_allocation (GstBaseTransform * trans, GstQuery * query)
1627 {
1628   GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1629
1630   g_mutex_lock (&postproc->postproc_lock);
1631   /* Let downstream handle the crop meta if they support it */
1632   postproc->forward_crop = (gst_query_find_allocation_meta (query,
1633           GST_VIDEO_CROP_META_API_TYPE, NULL) &&
1634       gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL));
1635   GST_DEBUG_OBJECT (postproc, "use_vpp_crop=%d", use_vpp_crop (postproc));
1636   g_mutex_unlock (&postproc->postproc_lock);
1637
1638   return gst_vaapi_plugin_base_decide_allocation (GST_VAAPI_PLUGIN_BASE (trans),
1639       query);
1640 }
1641
1642 static gboolean
1643 gst_vaapipostproc_src_event (GstBaseTransform * trans, GstEvent * event)
1644 {
1645   GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1646   gdouble new_x = 0, new_y = 0, x = 0, y = 0;
1647   GstStructure *structure;
1648   gboolean ret;
1649
1650   GST_DEBUG_OBJECT (postproc, "handling %s event", GST_EVENT_TYPE_NAME (event));
1651
1652   switch (GST_EVENT_TYPE (event)) {
1653     case GST_EVENT_NAVIGATION:
1654       event =
1655           GST_EVENT (gst_mini_object_make_writable (GST_MINI_OBJECT (event)));
1656
1657       structure = (GstStructure *) gst_event_get_structure (event);
1658       if (gst_structure_get_double (structure, "pointer_x", &x) &&
1659           gst_structure_get_double (structure, "pointer_y", &y)) {
1660         GST_DEBUG_OBJECT (postproc, "converting %fx%f", x, y);
1661
1662         if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_SIZE) {
1663           if ((GST_VIDEO_INFO_WIDTH (&postproc->srcpad_info)
1664                   != GST_VIDEO_INFO_WIDTH (&postproc->sinkpad_info))
1665               && (GST_VIDEO_INFO_HEIGHT (&postproc->srcpad_info)
1666                   != GST_VIDEO_INFO_HEIGHT (&postproc->sinkpad_info))) {
1667             new_x =
1668                 x * GST_VIDEO_INFO_WIDTH (&postproc->sinkpad_info) /
1669                 GST_VIDEO_INFO_WIDTH (&postproc->srcpad_info);
1670             new_y =
1671                 y * GST_VIDEO_INFO_HEIGHT (&postproc->sinkpad_info) /
1672                 GST_VIDEO_INFO_HEIGHT (&postproc->srcpad_info);
1673           }
1674         } else if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_VIDEO_DIRECTION) {
1675           switch (gst_vaapi_filter_get_video_direction (postproc->filter)) {
1676             case GST_VIDEO_ORIENTATION_90R:
1677               new_x = y;
1678               new_y = GST_VIDEO_INFO_WIDTH (&postproc->srcpad_info) - x;
1679               break;
1680             case GST_VIDEO_ORIENTATION_90L:
1681               new_x = GST_VIDEO_INFO_HEIGHT (&postproc->srcpad_info) - y;
1682               new_y = x;
1683               break;
1684             case GST_VIDEO_ORIENTATION_UR_LL:
1685               new_x = GST_VIDEO_INFO_HEIGHT (&postproc->srcpad_info) - y;
1686               new_y = GST_VIDEO_INFO_WIDTH (&postproc->srcpad_info) - x;
1687               break;
1688             case GST_VIDEO_ORIENTATION_UL_LR:
1689               new_x = y;
1690               new_y = x;
1691               break;
1692             case GST_VIDEO_ORIENTATION_180:
1693               new_x = GST_VIDEO_INFO_WIDTH (&postproc->srcpad_info) - x;
1694               new_y = GST_VIDEO_INFO_HEIGHT (&postproc->srcpad_info) - y;
1695               break;
1696             case GST_VIDEO_ORIENTATION_HORIZ:
1697               new_x = GST_VIDEO_INFO_WIDTH (&postproc->srcpad_info) - x;
1698               new_y = y;
1699               break;
1700             case GST_VIDEO_ORIENTATION_VERT:
1701               new_x = x;
1702               new_y = GST_VIDEO_INFO_HEIGHT (&postproc->srcpad_info) - y;
1703               break;
1704             default:
1705               new_x = x;
1706               new_y = y;
1707               break;
1708           }
1709         }
1710
1711         GST_DEBUG_OBJECT (postproc, "to %fx%f", new_x, new_y);
1712         gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, new_x,
1713             "pointer_y", G_TYPE_DOUBLE, new_y, NULL);
1714       }
1715       break;
1716     default:
1717       break;
1718   }
1719
1720   ret =
1721       GST_BASE_TRANSFORM_CLASS (gst_vaapipostproc_parent_class)->src_event
1722       (trans, event);
1723
1724   return ret;
1725 }
1726
1727 static gboolean
1728 gst_vaapipostproc_sink_event (GstBaseTransform * trans, GstEvent * event)
1729 {
1730   GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1731   GstTagList *taglist;
1732   gchar *orientation;
1733   gboolean ret;
1734   gboolean do_reconf;
1735
1736   GST_DEBUG_OBJECT (postproc, "handling %s event", GST_EVENT_TYPE_NAME (event));
1737
1738   switch (GST_EVENT_TYPE (event)) {
1739     case GST_EVENT_TAG:
1740       gst_event_parse_tag (event, &taglist);
1741
1742       if (gst_tag_list_get_string (taglist, "image-orientation", &orientation)) {
1743         do_reconf = TRUE;
1744         if (!g_strcmp0 ("rotate-0", orientation))
1745           postproc->tag_video_direction = GST_VIDEO_ORIENTATION_IDENTITY;
1746         else if (!g_strcmp0 ("rotate-90", orientation))
1747           postproc->tag_video_direction = GST_VIDEO_ORIENTATION_90R;
1748         else if (!g_strcmp0 ("rotate-180", orientation))
1749           postproc->tag_video_direction = GST_VIDEO_ORIENTATION_180;
1750         else if (!g_strcmp0 ("rotate-270", orientation))
1751           postproc->tag_video_direction = GST_VIDEO_ORIENTATION_90L;
1752         else if (!g_strcmp0 ("flip-rotate-0", orientation))
1753           postproc->tag_video_direction = GST_VIDEO_ORIENTATION_HORIZ;
1754         else if (!g_strcmp0 ("flip-rotate-90", orientation))
1755           postproc->tag_video_direction = GST_VIDEO_ORIENTATION_UL_LR;
1756         else if (!g_strcmp0 ("flip-rotate-180", orientation))
1757           postproc->tag_video_direction = GST_VIDEO_ORIENTATION_VERT;
1758         else if (!g_strcmp0 ("flip-rotate-270", orientation))
1759           postproc->tag_video_direction = GST_VIDEO_ORIENTATION_UR_LL;
1760         else
1761           do_reconf = FALSE;
1762
1763         g_free (orientation);
1764
1765         if (do_reconf) {
1766           postproc->flags |= GST_VAAPI_POSTPROC_FLAG_VIDEO_DIRECTION;
1767           gst_base_transform_reconfigure_src (trans);
1768         }
1769       }
1770       break;
1771     default:
1772       break;
1773   }
1774
1775   ret =
1776       GST_BASE_TRANSFORM_CLASS (gst_vaapipostproc_parent_class)->sink_event
1777       (trans, event);
1778
1779   return ret;
1780 }
1781
1782 static void
1783 gst_vaapipostproc_finalize (GObject * object)
1784 {
1785   GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (object);
1786
1787   gst_vaapipostproc_destroy (postproc);
1788
1789   g_mutex_clear (&postproc->postproc_lock);
1790   gst_vaapi_plugin_base_finalize (GST_VAAPI_PLUGIN_BASE (postproc));
1791   G_OBJECT_CLASS (gst_vaapipostproc_parent_class)->finalize (object);
1792 }
1793
1794 static void
1795 gst_vaapipostproc_set_property (GObject * object,
1796     guint prop_id, const GValue * value, GParamSpec * pspec)
1797 {
1798   GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (object);
1799   gboolean do_reconf = FALSE;
1800
1801   g_mutex_lock (&postproc->postproc_lock);
1802   switch (prop_id) {
1803     case PROP_FORMAT:
1804       postproc->format = g_value_get_enum (value);
1805       break;
1806     case PROP_WIDTH:
1807     {
1808       guint prev_width = postproc->width;
1809       postproc->width = g_value_get_uint (value);
1810       do_reconf = (prev_width != postproc->width);
1811       break;
1812     }
1813     case PROP_HEIGHT:
1814     {
1815       guint prev_height = postproc->height;
1816       postproc->height = g_value_get_uint (value);
1817       do_reconf = (prev_height != postproc->height);
1818       break;
1819     }
1820     case PROP_FORCE_ASPECT_RATIO:
1821       postproc->keep_aspect = g_value_get_boolean (value);
1822       break;
1823     case PROP_DEINTERLACE_MODE:
1824       postproc->deinterlace_mode = g_value_get_enum (value);
1825       break;
1826     case PROP_DEINTERLACE_METHOD:
1827       postproc->deinterlace_method = g_value_get_enum (value);
1828       break;
1829     case PROP_DENOISE:
1830       postproc->denoise_level = g_value_get_float (value);
1831       postproc->flags |= GST_VAAPI_POSTPROC_FLAG_DENOISE;
1832       break;
1833     case PROP_SHARPEN:
1834       postproc->sharpen_level = g_value_get_float (value);
1835       postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SHARPEN;
1836       break;
1837     case PROP_HUE:
1838       postproc->hue = g_value_get_float (value);
1839       postproc->flags |= GST_VAAPI_POSTPROC_FLAG_HUE;
1840       break;
1841     case PROP_SATURATION:
1842       postproc->saturation = g_value_get_float (value);
1843       postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SATURATION;
1844       break;
1845     case PROP_BRIGHTNESS:
1846       postproc->brightness = g_value_get_float (value);
1847       postproc->flags |= GST_VAAPI_POSTPROC_FLAG_BRIGHTNESS;
1848       break;
1849     case PROP_CONTRAST:
1850       postproc->contrast = g_value_get_float (value);
1851       postproc->flags |= GST_VAAPI_POSTPROC_FLAG_CONTRAST;
1852       break;
1853     case PROP_SCALE_METHOD:
1854       postproc->scale_method = g_value_get_enum (value);
1855       postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SCALE;
1856       break;
1857     case PROP_VIDEO_DIRECTION:
1858       postproc->video_direction = g_value_get_enum (value);
1859       postproc->flags |= GST_VAAPI_POSTPROC_FLAG_VIDEO_DIRECTION;
1860       break;
1861     case PROP_SKIN_TONE_ENHANCEMENT:
1862       postproc->skintone_enhance = g_value_get_boolean (value);
1863       postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SKINTONE;
1864       break;
1865     default:
1866       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1867       break;
1868   }
1869   g_mutex_unlock (&postproc->postproc_lock);
1870
1871   if (do_reconf || check_filter_update (postproc))
1872     gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (postproc));
1873 }
1874
1875 static void
1876 gst_vaapipostproc_get_property (GObject * object,
1877     guint prop_id, GValue * value, GParamSpec * pspec)
1878 {
1879   GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (object);
1880
1881   g_mutex_lock (&postproc->postproc_lock);
1882   switch (prop_id) {
1883     case PROP_FORMAT:
1884       g_value_set_enum (value, postproc->format);
1885       break;
1886     case PROP_WIDTH:
1887       g_value_set_uint (value, postproc->width);
1888       break;
1889     case PROP_HEIGHT:
1890       g_value_set_uint (value, postproc->height);
1891       break;
1892     case PROP_FORCE_ASPECT_RATIO:
1893       g_value_set_boolean (value, postproc->keep_aspect);
1894       break;
1895     case PROP_DEINTERLACE_MODE:
1896       g_value_set_enum (value, postproc->deinterlace_mode);
1897       break;
1898     case PROP_DEINTERLACE_METHOD:
1899       g_value_set_enum (value, postproc->deinterlace_method);
1900       break;
1901     case PROP_DENOISE:
1902       g_value_set_float (value, postproc->denoise_level);
1903       break;
1904     case PROP_SHARPEN:
1905       g_value_set_float (value, postproc->sharpen_level);
1906       break;
1907     case PROP_HUE:
1908       g_value_set_float (value, postproc->hue);
1909       break;
1910     case PROP_SATURATION:
1911       g_value_set_float (value, postproc->saturation);
1912       break;
1913     case PROP_BRIGHTNESS:
1914       g_value_set_float (value, postproc->brightness);
1915       break;
1916     case PROP_CONTRAST:
1917       g_value_set_float (value, postproc->contrast);
1918       break;
1919     case PROP_SCALE_METHOD:
1920       g_value_set_enum (value, postproc->scale_method);
1921       break;
1922     case PROP_VIDEO_DIRECTION:
1923       g_value_set_enum (value, postproc->video_direction);
1924       break;
1925     case PROP_SKIN_TONE_ENHANCEMENT:
1926       g_value_set_boolean (value, postproc->skintone_enhance);
1927       break;
1928     default:
1929       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1930       break;
1931   }
1932   g_mutex_unlock (&postproc->postproc_lock);
1933 }
1934
1935 static void
1936 gst_vaapipostproc_class_init (GstVaapiPostprocClass * klass)
1937 {
1938   GObjectClass *const object_class = G_OBJECT_CLASS (klass);
1939   GstElementClass *const element_class = GST_ELEMENT_CLASS (klass);
1940   GstBaseTransformClass *const trans_class = GST_BASE_TRANSFORM_CLASS (klass);
1941   GPtrArray *filter_ops;
1942   GstVaapiFilterOpInfo *filter_op;
1943
1944   GST_DEBUG_CATEGORY_INIT (gst_debug_vaapipostproc,
1945       GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
1946
1947   gst_vaapi_plugin_base_class_init (GST_VAAPI_PLUGIN_BASE_CLASS (klass));
1948
1949   object_class->finalize = gst_vaapipostproc_finalize;
1950   object_class->set_property = gst_vaapipostproc_set_property;
1951   object_class->get_property = gst_vaapipostproc_get_property;
1952   trans_class->start = gst_vaapipostproc_start;
1953   trans_class->stop = gst_vaapipostproc_stop;
1954   trans_class->fixate_caps = gst_vaapipostproc_fixate_caps;
1955   trans_class->transform_caps = gst_vaapipostproc_transform_caps;
1956   trans_class->transform_size = gst_vaapipostproc_transform_size;
1957   trans_class->transform_meta = gst_vaapipostproc_transform_meta;
1958   trans_class->transform = gst_vaapipostproc_transform;
1959   trans_class->set_caps = gst_vaapipostproc_set_caps;
1960   trans_class->query = gst_vaapipostproc_query;
1961   trans_class->propose_allocation = gst_vaapipostproc_propose_allocation;
1962   trans_class->decide_allocation = gst_vaapipostproc_decide_allocation;
1963   trans_class->src_event = gst_vaapipostproc_src_event;
1964   trans_class->sink_event = gst_vaapipostproc_sink_event;
1965
1966   trans_class->prepare_output_buffer = gst_vaapipostproc_prepare_output_buffer;
1967
1968   element_class->set_context = gst_vaapi_base_set_context;
1969   gst_element_class_set_static_metadata (element_class,
1970       "VA-API video postprocessing",
1971       "Filter/Converter/Effect/Video/Scaler/Deinterlace/Hardware",
1972       GST_PLUGIN_DESC, "Gwenole Beauchesne <gwenole.beauchesne@intel.com>");
1973
1974   /* sink pad */
1975   gst_element_class_add_static_pad_template (element_class,
1976       &gst_vaapipostproc_sink_factory);
1977
1978   /* src pad */
1979   gst_element_class_add_static_pad_template (element_class,
1980       &gst_vaapipostproc_src_factory);
1981
1982   /**
1983    * GstVaapiPostproc:deinterlace-mode:
1984    *
1985    * This selects whether the deinterlacing should always be applied
1986    * or if they should only be applied on content that has the
1987    * "interlaced" flag on the caps.
1988    */
1989   g_object_class_install_property
1990       (object_class,
1991       PROP_DEINTERLACE_MODE,
1992       g_param_spec_enum ("deinterlace-mode",
1993           "Deinterlace mode",
1994           "Deinterlace mode to use",
1995           GST_VAAPI_TYPE_DEINTERLACE_MODE,
1996           DEFAULT_DEINTERLACE_MODE,
1997           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1998
1999   /**
2000    * GstVaapiPostproc:deinterlace-method:
2001    *
2002    * This selects the deinterlacing method to apply.
2003    */
2004   g_object_class_install_property
2005       (object_class,
2006       PROP_DEINTERLACE_METHOD,
2007       g_param_spec_enum ("deinterlace-method",
2008           "Deinterlace method",
2009           "Deinterlace method to use",
2010           GST_VAAPI_TYPE_DEINTERLACE_METHOD,
2011           DEFAULT_DEINTERLACE_METHOD,
2012           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2013
2014   filter_ops = gst_vaapi_filter_get_operations (NULL);
2015   if (!filter_ops)
2016     return;
2017
2018   /**
2019    * GstVaapiPostproc:format:
2020    *
2021    * The forced output pixel format, expressed as a #GstVideoFormat.
2022    */
2023   filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_FORMAT);
2024   if (filter_op)
2025     g_object_class_install_property (object_class,
2026         PROP_FORMAT, filter_op->pspec);
2027
2028   /**
2029    * GstVaapiPostproc:width:
2030    *
2031    * The forced output width in pixels. If set to zero, the width is
2032    * calculated from the height if aspect ration is preserved, or
2033    * inherited from the sink caps width
2034    */
2035   g_object_class_install_property
2036       (object_class,
2037       PROP_WIDTH,
2038       g_param_spec_uint ("width",
2039           "Width",
2040           "Forced output width",
2041           0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2042
2043   /**
2044    * GstVaapiPostproc:height:
2045    *
2046    * The forced output height in pixels. If set to zero, the height is
2047    * calculated from the width if aspect ration is preserved, or
2048    * inherited from the sink caps height
2049    */
2050   g_object_class_install_property
2051       (object_class,
2052       PROP_HEIGHT,
2053       g_param_spec_uint ("height",
2054           "Height",
2055           "Forced output height",
2056           0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2057
2058   /**
2059    * GstVaapiPostproc:force-aspect-ratio:
2060    *
2061    * When enabled, scaling respects video aspect ratio; when disabled,
2062    * the video is distorted to fit the width and height properties.
2063    */
2064   g_object_class_install_property
2065       (object_class,
2066       PROP_FORCE_ASPECT_RATIO,
2067       g_param_spec_boolean ("force-aspect-ratio",
2068           "Force aspect ratio",
2069           "When enabled, scaling will respect original aspect ratio",
2070           TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2071
2072   /**
2073    * GstVaapiPostproc:denoise:
2074    *
2075    * The level of noise reduction to apply.
2076    */
2077   filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_DENOISE);
2078   if (filter_op)
2079     g_object_class_install_property (object_class,
2080         PROP_DENOISE, filter_op->pspec);
2081
2082   /**
2083    * GstVaapiPostproc:sharpen:
2084    *
2085    * The level of sharpening to apply for positive values, or the
2086    * level of blurring for negative values.
2087    */
2088   filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_SHARPEN);
2089   if (filter_op)
2090     g_object_class_install_property (object_class,
2091         PROP_SHARPEN, filter_op->pspec);
2092
2093   /**
2094    * GstVaapiPostproc:hue:
2095    *
2096    * The color hue, expressed as a float value. Range is -180.0 to
2097    * 180.0. Default value is 0.0 and represents no modification.
2098    */
2099   filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_HUE);
2100   if (filter_op)
2101     g_object_class_install_property (object_class, PROP_HUE, filter_op->pspec);
2102
2103   /**
2104    * GstVaapiPostproc:saturation:
2105    *
2106    * The color saturation, expressed as a float value. Range is 0.0 to
2107    * 2.0. Default value is 1.0 and represents no modification.
2108    */
2109   filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_SATURATION);
2110   if (filter_op)
2111     g_object_class_install_property (object_class,
2112         PROP_SATURATION, filter_op->pspec);
2113
2114   /**
2115    * GstVaapiPostproc:brightness:
2116    *
2117    * The color brightness, expressed as a float value. Range is -1.0
2118    * to 1.0. Default value is 0.0 and represents no modification.
2119    */
2120   filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_BRIGHTNESS);
2121   if (filter_op)
2122     g_object_class_install_property (object_class,
2123         PROP_BRIGHTNESS, filter_op->pspec);
2124
2125   /**
2126    * GstVaapiPostproc:contrast:
2127    *
2128    * The color contrast, expressed as a float value. Range is 0.0 to
2129    * 2.0. Default value is 1.0 and represents no modification.
2130    */
2131   filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_CONTRAST);
2132   if (filter_op)
2133     g_object_class_install_property (object_class,
2134         PROP_CONTRAST, filter_op->pspec);
2135
2136   /**
2137    * GstVaapiPostproc:scale-method:
2138    *
2139    * The scaling method to use, expressed as an enum value. See
2140    * #GstVaapiScaleMethod.
2141    */
2142   filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_SCALING);
2143   if (filter_op)
2144     g_object_class_install_property (object_class,
2145         PROP_SCALE_METHOD, filter_op->pspec);
2146
2147   /**
2148    * GstVaapiPostproc:video-direction:
2149    *
2150    * The video-direction to use, expressed as an enum value. See
2151    * #GstVideoDirectionMethod.
2152    */
2153   filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_VIDEO_DIRECTION);
2154   if (filter_op)
2155     g_object_class_install_property (object_class,
2156         PROP_VIDEO_DIRECTION, filter_op->pspec);
2157
2158   /**
2159    * GstVaapiPostproc:skin-tone-enhancement:
2160    *
2161    * Apply the skin tone enhancement algorithm.
2162    */
2163   filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_SKINTONE);
2164   if (filter_op)
2165     g_object_class_install_property (object_class,
2166         PROP_SKIN_TONE_ENHANCEMENT, filter_op->pspec);
2167
2168   g_ptr_array_unref (filter_ops);
2169 }
2170
2171 static float *
2172 find_value_ptr (GstVaapiPostproc * postproc, GstVaapiFilterOp op)
2173 {
2174   switch (op) {
2175     case GST_VAAPI_FILTER_OP_HUE:
2176       return &postproc->hue;
2177     case GST_VAAPI_FILTER_OP_SATURATION:
2178       return &postproc->saturation;
2179     case GST_VAAPI_FILTER_OP_BRIGHTNESS:
2180       return &postproc->brightness;
2181     case GST_VAAPI_FILTER_OP_CONTRAST:
2182       return &postproc->contrast;
2183     default:
2184       return NULL;
2185   }
2186 }
2187
2188 static void
2189 cb_set_default_value (GstVaapiPostproc * postproc, GPtrArray * filter_ops,
2190     GstVaapiFilterOp op)
2191 {
2192   GstVaapiFilterOpInfo *filter_op;
2193   GParamSpecFloat *pspec;
2194   float *var;
2195
2196   filter_op = find_filter_op (filter_ops, op);
2197   if (!filter_op)
2198     return;
2199   var = find_value_ptr (postproc, op);
2200   if (!var)
2201     return;
2202   pspec = G_PARAM_SPEC_FLOAT (filter_op->pspec);
2203   *var = pspec->default_value;
2204 }
2205
2206 static void
2207 gst_vaapipostproc_init (GstVaapiPostproc * postproc)
2208 {
2209   GPtrArray *filter_ops;
2210   guint i;
2211
2212   gst_vaapi_plugin_base_init (GST_VAAPI_PLUGIN_BASE (postproc),
2213       GST_CAT_DEFAULT);
2214
2215   g_mutex_init (&postproc->postproc_lock);
2216   postproc->format = DEFAULT_FORMAT;
2217   postproc->deinterlace_mode = DEFAULT_DEINTERLACE_MODE;
2218   postproc->deinterlace_method = DEFAULT_DEINTERLACE_METHOD;
2219   postproc->field_duration = GST_CLOCK_TIME_NONE;
2220   postproc->keep_aspect = TRUE;
2221   postproc->get_va_surfaces = TRUE;
2222   postproc->forward_crop = FALSE;
2223
2224   /* AUTO is not valid for tag_video_direction, this is just to
2225    * ensure we setup the method as sink event tag */
2226   postproc->tag_video_direction = GST_VIDEO_ORIENTATION_AUTO;
2227
2228   filter_ops = gst_vaapi_filter_get_operations (NULL);
2229   if (filter_ops) {
2230     for (i = GST_VAAPI_FILTER_OP_HUE; i <= GST_VAAPI_FILTER_OP_CONTRAST; i++)
2231       cb_set_default_value (postproc, filter_ops, i);
2232     g_ptr_array_unref (filter_ops);
2233   }
2234
2235   gst_video_info_init (&postproc->sinkpad_info);
2236   gst_video_info_init (&postproc->srcpad_info);
2237   gst_video_info_init (&postproc->filter_pool_info);
2238 }
2239
2240 /* ------------------------------------------------------------------------ */
2241 /* --- GstColorBalance interface                                        --- */
2242 /* ------------------------------------------------------------------------ */
2243
2244 #define CB_CHANNEL_FACTOR 1000.0
2245
2246 typedef struct
2247 {
2248   GstVaapiFilterOp op;
2249   const gchar *name;
2250 } ColorBalanceChannel;
2251
2252 ColorBalanceChannel cb_channels[] = {
2253   {
2254       GST_VAAPI_FILTER_OP_HUE, "VA_FILTER_HUE"}, {
2255       GST_VAAPI_FILTER_OP_SATURATION, "VA_FILTER_SATURATION"}, {
2256       GST_VAAPI_FILTER_OP_BRIGHTNESS, "VA_FILTER_BRIGHTNESS"}, {
2257       GST_VAAPI_FILTER_OP_CONTRAST, "VA_FILTER_CONTRAST"},
2258 };
2259
2260 static void
2261 cb_channels_init (GstVaapiPostproc * postproc)
2262 {
2263   GPtrArray *filter_ops;
2264   GstVaapiFilterOpInfo *filter_op;
2265   GParamSpecFloat *pspec;
2266   GstColorBalanceChannel *channel;
2267   guint i;
2268
2269   if (postproc->cb_channels)
2270     return;
2271
2272   g_mutex_lock (&postproc->postproc_lock);
2273   if (!gst_vaapipostproc_ensure_filter (postproc)) {
2274     g_mutex_unlock (&postproc->postproc_lock);
2275     return;
2276   }
2277   g_mutex_unlock (&postproc->postproc_lock);
2278
2279   filter_ops = postproc->filter_ops ? g_ptr_array_ref (postproc->filter_ops)
2280       : gst_vaapi_filter_get_operations (postproc->filter);
2281   if (!filter_ops)
2282     return;
2283
2284   for (i = 0; i < G_N_ELEMENTS (cb_channels); i++) {
2285     filter_op = find_filter_op (filter_ops, cb_channels[i].op);
2286     if (!filter_op)
2287       continue;
2288
2289     pspec = G_PARAM_SPEC_FLOAT (filter_op->pspec);
2290     channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
2291     channel->label = g_strdup (cb_channels[i].name);
2292     channel->min_value = pspec->minimum * CB_CHANNEL_FACTOR;
2293     channel->max_value = pspec->maximum * CB_CHANNEL_FACTOR;
2294
2295     postproc->cb_channels = g_list_prepend (postproc->cb_channels, channel);
2296   }
2297
2298   g_ptr_array_unref (filter_ops);
2299 }
2300
2301 static const GList *
2302 gst_vaapipostproc_colorbalance_list_channels (GstColorBalance * balance)
2303 {
2304   GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (balance);
2305
2306   cb_channels_init (postproc);
2307   return postproc->cb_channels;
2308 }
2309
2310 static gfloat *
2311 cb_get_value_ptr (GstVaapiPostproc * postproc,
2312     GstColorBalanceChannel * channel, GstVaapiPostprocFlags * flags)
2313 {
2314   guint i;
2315   gfloat *ret = NULL;
2316
2317   for (i = 0; i < G_N_ELEMENTS (cb_channels); i++) {
2318     if (g_ascii_strcasecmp (cb_channels[i].name, channel->label) == 0)
2319       break;
2320   }
2321   if (i >= G_N_ELEMENTS (cb_channels))
2322     return NULL;
2323
2324   ret = find_value_ptr (postproc, cb_channels[i].op);
2325   if (flags)
2326     *flags = 1 << cb_channels[i].op;
2327   return ret;
2328 }
2329
2330 static void
2331 gst_vaapipostproc_colorbalance_set_value (GstColorBalance * balance,
2332     GstColorBalanceChannel * channel, gint value)
2333 {
2334   GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (balance);
2335   GstVaapiPostprocFlags flags;
2336   gfloat new_val, *var;
2337
2338   value = CLAMP (value, channel->min_value, channel->max_value);
2339   new_val = (gfloat) value / CB_CHANNEL_FACTOR;
2340
2341   var = cb_get_value_ptr (postproc, channel, &flags);
2342   if (var) {
2343     *var = new_val;
2344     g_mutex_lock (&postproc->postproc_lock);
2345     postproc->flags |= flags;
2346     g_mutex_unlock (&postproc->postproc_lock);
2347     gst_color_balance_value_changed (balance, channel, value);
2348     if (check_filter_update (postproc))
2349       gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (postproc));
2350     return;
2351   }
2352
2353   GST_WARNING_OBJECT (postproc, "unknown channel %s", channel->label);
2354 }
2355
2356 static gint
2357 gst_vaapipostproc_colorbalance_get_value (GstColorBalance * balance,
2358     GstColorBalanceChannel * channel)
2359 {
2360   GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (balance);
2361   gfloat *var;
2362   gint new_val;
2363
2364   var = cb_get_value_ptr (postproc, channel, NULL);
2365   if (var) {
2366     new_val = (gint) ((*var) * CB_CHANNEL_FACTOR);
2367     new_val = CLAMP (new_val, channel->min_value, channel->max_value);
2368     return new_val;
2369   }
2370
2371   GST_WARNING_OBJECT (postproc, "unknown channel %s", channel->label);
2372   return G_MININT;
2373 }
2374
2375 static GstColorBalanceType
2376 gst_vaapipostproc_colorbalance_get_balance_type (GstColorBalance * balance)
2377 {
2378   return GST_COLOR_BALANCE_HARDWARE;
2379 }
2380
2381 static void
2382 gst_vaapipostproc_colorbalance_init (gpointer iface, gpointer data)
2383 {
2384   GstColorBalanceInterface *cbface = iface;
2385   cbface->list_channels = gst_vaapipostproc_colorbalance_list_channels;
2386   cbface->set_value = gst_vaapipostproc_colorbalance_set_value;
2387   cbface->get_value = gst_vaapipostproc_colorbalance_get_value;
2388   cbface->get_balance_type = gst_vaapipostproc_colorbalance_get_balance_type;
2389 }