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