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