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